init on github

This commit is contained in:
Elie Schoppik
2024-11-20 13:08:06 -05:00
parent e99025b8f1
commit 9be6268005
5 changed files with 1877 additions and 0 deletions

953
src/github/index.ts Normal file
View File

@@ -0,0 +1,953 @@
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import fetch from "node-fetch";
import {
GitHubContent,
GitHubCreateUpdateFileResponse,
GitHubSearchResponse,
GitHubRepository,
GitHubTree,
GitHubCommit,
GitHubReference,
CreateRepositoryOptions,
FileOperation,
CreateTreeParams,
GitHubPullRequest,
CreateIssueOptions,
CreatePullRequestOptions,
GitHubIssue,
GitHubFork,
CreateBranchOptions,
} from './interfaces.js';
const server = new Server({
name: "github-mcp-server",
version: "0.1.0",
}, {
capabilities: {
tools: {}
}
});
const GITHUB_PERSONAL_ACCESS_TOKEN = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
if (!GITHUB_PERSONAL_ACCESS_TOKEN) {
console.error("GITHUB_PERSONAL_ACCESS_TOKEN environment variable is not set");
process.exit(1);
}
// GitHub API helper functions
// Add these helper functions to your existing code
async function forkRepository(
owner: string,
repo: string,
organization?: string
): Promise<GitHubFork> {
const url = organization
? `https://api.github.com/repos/${owner}/${repo}/forks?organization=${organization}`
: `https://api.github.com/repos/${owner}/${repo}/forks`;
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server"
}
});
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubFork;
}
async function createBranch(
owner: string,
repo: string,
options: CreateBranchOptions
): Promise<GitHubReference> {
// The ref needs to be in the format "refs/heads/branch-name"
const fullRef = `refs/heads/${options.ref}`;
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/git/refs`,
{
method: "POST",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server",
"Content-Type": "application/json"
},
body: JSON.stringify({
ref: fullRef,
sha: options.sha
})
}
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubReference;
}
// Helper function to get the default branch SHA
async function getDefaultBranchSHA(
owner: string,
repo: string
): Promise<string> {
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/main`,
{
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server"
}
}
);
// If main branch doesn't exist, try master
if (!response.ok) {
const masterResponse = await fetch(
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/master`,
{
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server"
}
}
);
if (!masterResponse.ok) {
throw new Error("Could not find default branch (tried 'main' and 'master')");
}
const data = await masterResponse.json() as GitHubReference;
return data.object.sha;
}
const data = await response.json() as GitHubReference;
return data.object.sha;
}
async function getFileContents(owner: string, repo: string, path: string): Promise<GitHubContent> {
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;
const response = await fetch(url, {
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server"
}
});
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
const data = await response.json() as GitHubContent;
// If it's a file, decode the content
if (!Array.isArray(data) && data.content) {
return {
...data,
content: Buffer.from(data.content, 'base64').toString('utf8')
};
}
return data;
}
async function createIssue(
owner: string,
repo: string,
options: CreateIssueOptions
): Promise<GitHubIssue> {
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/issues`,
{
method: "POST",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server",
"Content-Type": "application/json"
},
body: JSON.stringify(options)
}
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubIssue;
}
async function createPullRequest(
owner: string,
repo: string,
options: CreatePullRequestOptions
): Promise<GitHubPullRequest> {
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/pulls`,
{
method: "POST",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server",
"Content-Type": "application/json"
},
body: JSON.stringify(options)
}
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubPullRequest;
}
async function createOrUpdateFile(
owner: string,
repo: string,
path: string,
content: string,
message: string,
sha?: string
): Promise<GitHubCreateUpdateFileResponse> {
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;
const body = {
message,
content: Buffer.from(content).toString('base64'),
sha
};
const response = await fetch(url, {
method: "PUT",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server",
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubCreateUpdateFileResponse;
}
async function createTree(
owner: string,
repo: string,
files: FileOperation[],
baseTree?: string
): Promise<GitHubTree> {
const tree: CreateTreeParams[] = files.map(file => ({
path: file.path,
mode: '100644',
type: 'blob',
content: file.content
}));
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/git/trees`,
{
method: "POST",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server",
"Content-Type": "application/json"
},
body: JSON.stringify({
tree,
base_tree: baseTree
})
}
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubTree;
}
async function createCommit(
owner: string,
repo: string,
message: string,
tree: string,
parents: string[]
): Promise<GitHubCommit> {
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/git/commits`,
{
method: "POST",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server",
"Content-Type": "application/json"
},
body: JSON.stringify({
message,
tree,
parents
})
}
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubCommit;
}
async function updateReference(
owner: string,
repo: string,
ref: string,
sha: string
): Promise<GitHubReference> {
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/git/refs/${ref}`,
{
method: "PATCH",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server",
"Content-Type": "application/json"
},
body: JSON.stringify({
sha,
force: true
})
}
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubReference;
}
async function pushFiles(
owner: string,
repo: string,
branch: string,
files: FileOperation[],
message: string
): Promise<GitHubReference> {
const refResponse = await fetch(
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
{
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server"
}
}
);
if (!refResponse.ok) {
throw new Error(`GitHub API error: ${refResponse.statusText}`);
}
const ref = await refResponse.json() as GitHubReference;
const commitSha = ref.object.sha;
const tree = await createTree(owner, repo, files, commitSha);
const commit = await createCommit(owner, repo, message, tree.sha, [commitSha]);
return await updateReference(owner, repo, `heads/${branch}`, commit.sha);
}
async function searchRepositories(
query: string,
page: number = 1,
perPage: number = 30
): Promise<GitHubSearchResponse> {
const url = new URL("https://api.github.com/search/repositories");
url.searchParams.append("q", query);
url.searchParams.append("page", page.toString());
url.searchParams.append("per_page", perPage.toString());
const response = await fetch(url.toString(), {
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server"
}
});
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubSearchResponse;
}
async function createRepository(options: CreateRepositoryOptions): Promise<GitHubRepository> {
const response = await fetch("https://api.github.com/user/repos", {
method: "POST",
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server",
"Content-Type": "application/json"
},
body: JSON.stringify(options)
});
if (!response.ok) {
throw new Error(`GitHub API error: ${response.statusText}`);
}
return await response.json() as GitHubRepository;
}
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "search_repositories",
description: "Search for GitHub repositories",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query (see GitHub search syntax)"
},
page: {
type: "number",
description: "Page number for pagination (default: 1)"
},
perPage: {
type: "number",
description: "Number of results per page (default: 30, max: 100)"
}
},
required: ["query"]
}
},
{
name: "create_repository",
description: "Create a new GitHub repository in your account",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Repository name"
},
description: {
type: "string",
description: "Repository description"
},
private: {
type: "boolean",
description: "Whether the repository should be private"
},
autoInit: {
type: "boolean",
description: "Initialize with README.md"
}
},
required: ["name"]
}
},
{
name: "get_file_contents",
description: "Get the contents of a file or directory from a GitHub repository",
inputSchema: {
type: "object",
properties: {
owner: {
type: "string",
description: "Repository owner (username or organization)"
},
repo: {
type: "string",
description: "Repository name"
},
path: {
type: "string",
description: "Path to the file or directory"
}
},
required: ["owner", "repo", "path"]
}
},
{
name: "create_or_update_file",
description: "Create or update a single file in a GitHub repository",
inputSchema: {
type: "object",
properties: {
owner: {
type: "string",
description: "Repository owner (username or organization)"
},
repo: {
type: "string",
description: "Repository name"
},
path: {
type: "string",
description: "Path where to create/update the file"
},
content: {
type: "string",
description: "Content of the file"
},
message: {
type: "string",
description: "Commit message"
},
sha: {
type: "string",
description: "SHA of the file being replaced (required when updating existing files)"
}
},
required: ["owner", "repo", "path", "content", "message"]
}
},
{
name: "push_files",
description: "Push multiple files to a GitHub repository in a single commit",
inputSchema: {
type: "object",
properties: {
owner: {
type: "string",
description: "Repository owner (username or organization)"
},
repo: {
type: "string",
description: "Repository name"
},
branch: {
type: "string",
description: "Branch to push to (e.g., 'main' or 'master')"
},
files: {
type: "array",
description: "Array of files to push",
items: {
type: "object",
properties: {
path: {
type: "string",
description: "Path where to create the file"
},
content: {
type: "string",
description: "Content of the file"
}
},
required: ["path", "content"]
}
},
message: {
type: "string",
description: "Commit message"
}
},
required: ["owner", "repo", "branch", "files", "message"]
}
},
{
name: "open_in_browser",
description: "Open a GitHub repository, file, issue, or pull request in the browser",
inputSchema: {
type: "object",
properties: {
owner: {
type: "string",
description: "Repository owner (username or organization)"
},
repo: {
type: "string",
description: "Repository name"
},
type: {
type: "string",
enum: ["repository", "file", "issue", "pull_request"],
description: "Type of resource to open"
},
path: {
type: "string",
description: "Path to the file (only for type='file')"
},
number: {
type: "number",
description: "Issue or PR number (only for type='issue' or type='pull_request')"
}
},
required: ["owner", "repo", "type"]
}
},
{
name: "create_issue",
description: "Create a new issue in a GitHub repository",
inputSchema: {
type: "object",
properties: {
owner: {
type: "string",
description: "Repository owner (username or organization)"
},
repo: {
type: "string",
description: "Repository name"
},
title: {
type: "string",
description: "Issue title"
},
body: {
type: "string",
description: "Issue body/description"
},
assignees: {
type: "array",
items: { type: "string" },
description: "Array of usernames to assign"
},
labels: {
type: "array",
items: { type: "string" },
description: "Array of label names"
},
milestone: {
type: "number",
description: "Milestone number to assign"
}
},
required: ["owner", "repo", "title"]
}
},
{
name: "create_pull_request",
description: "Create a new pull request in a GitHub repository",
inputSchema: {
type: "object",
properties: {
owner: {
type: "string",
description: "Repository owner (username or organization)"
},
repo: {
type: "string",
description: "Repository name"
},
title: {
type: "string",
description: "Pull request title"
},
body: {
type: "string",
description: "Pull request body/description"
},
head: {
type: "string",
description: "The name of the branch where your changes are implemented"
},
base: {
type: "string",
description: "The name of the branch you want the changes pulled into"
},
draft: {
type: "boolean",
description: "Whether to create the pull request as a draft"
},
maintainer_can_modify: {
type: "boolean",
description: "Whether maintainers can modify the pull request"
}
},
required: ["owner", "repo", "title", "head", "base"]
}
},
{
name: "fork_repository",
description: "Fork a GitHub repository to your account or specified organization",
inputSchema: {
type: "object",
properties: {
owner: {
type: "string",
description: "Repository owner (username or organization)"
},
repo: {
type: "string",
description: "Repository name"
},
organization: {
type: "string",
description: "Optional: organization to fork to (defaults to your personal account)"
}
},
required: ["owner", "repo"]
}
},
{
name: "create_branch",
description: "Create a new branch in a GitHub repository",
inputSchema: {
type: "object",
properties: {
owner: {
type: "string",
description: "Repository owner (username or organization)"
},
repo: {
type: "string",
description: "Repository name"
},
branch: {
type: "string",
description: "Name for the new branch"
},
from_branch: {
type: "string",
description: "Optional: source branch to create from (defaults to the repository's default branch)"
}
},
required: ["owner", "repo", "branch"]
}
}
]
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "fork_repository") {
if (!request.params.arguments) {
throw new Error("Arguments are required");
}
const args = request.params.arguments as {
owner: string;
repo: string;
organization?: string;
};
const fork = await forkRepository(args.owner, args.repo, args.organization);
return { toolResult: fork };
}
if (request.params.name === "create_branch") {
if (!request.params.arguments) {
throw new Error("Arguments are required");
}
const args = request.params.arguments as {
owner: string;
repo: string;
branch: string;
from_branch?: string;
};
// If no source branch is specified, use the default branch
let sha: string;
if (args.from_branch) {
const response = await fetch(
`https://api.github.com/repos/${args.owner}/${args.repo}/git/refs/heads/${args.from_branch}`,
{
headers: {
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
"Accept": "application/vnd.github.v3+json",
"User-Agent": "github-mcp-server"
}
}
);
if (!response.ok) {
throw new Error(`Source branch '${args.from_branch}' not found`);
}
const data = await response.json() as GitHubReference;
sha = data.object.sha;
} else {
sha = await getDefaultBranchSHA(args.owner, args.repo);
}
const branch = await createBranch(args.owner, args.repo, {
ref: args.branch,
sha: sha
});
return { toolResult: branch };
}
if (request.params.name === "search_repositories") {
const { query, page, perPage } = request.params.arguments as {
query: string;
page?: number;
perPage?: number;
};
const results = await searchRepositories(query, page, perPage);
return { toolResult: results };
}
if (request.params.name === "create_repository") {
const options = request.params.arguments as CreateRepositoryOptions;
const repository = await createRepository(options);
return { toolResult: repository };
}
if (request.params.name === "get_file_contents") {
const { owner, repo, path } = request.params.arguments as {
owner: string;
repo: string;
path: string;
};
const contents = await getFileContents(owner, repo, path);
return { toolResult: contents };
}
if (request.params.name === "create_or_update_file") {
const { owner, repo, path, content, message, sha } = request.params.arguments as {
owner: string;
repo: string;
path: string;
content: string;
message: string;
sha?: string;
};
const result = await createOrUpdateFile(owner, repo, path, content, message, sha);
return { toolResult: result };
}
if (request.params.name === "push_files") {
const { owner, repo, branch, files, message } = request.params.arguments as {
owner: string;
repo: string;
branch: string;
files: FileOperation[];
message: string;
};
const result = await pushFiles(owner, repo, branch, files, message);
return { toolResult: result };
}
if (request.params.name === "open_in_browser") {
const { owner, repo, type, path, number } = request.params.arguments as {
owner: string;
repo: string;
type: "repository" | "file" | "issue" | "pull_request";
path?: string;
number?: number;
};
let url: string;
switch (type) {
case "repository":
url = `https://github.com/${owner}/${repo}`;
break;
case "file":
if (!path) throw new Error("Path is required for file URLs");
url = `https://github.com/${owner}/${repo}/blob/main/${path}`;
break;
case "issue":
if (!number) throw new Error("Number is required for issue URLs");
url = `https://github.com/${owner}/${repo}/issues/${number}`;
break;
case "pull_request":
if (!number) throw new Error("Number is required for pull request URLs");
url = `https://github.com/${owner}/${repo}/pull/${number}`;
break;
default:
throw new Error(`Invalid type: ${type}`);
}
return { toolResult: { url } };
}
if (request.params.name === "create_issue") {
if (!request.params.arguments) {
throw new Error("Arguments are required");
}
const args = request.params.arguments as {
owner: string;
repo: string;
title: string;
body?: string;
assignees?: string[];
milestone?: number;
labels?: string[];
};
const { owner, repo, ...options } = args;
const issue = await createIssue(owner, repo, options);
return { toolResult: issue };
}
if (request.params.name === "create_pull_request") {
if (!request.params.arguments) {
throw new Error("Arguments are required");
}
const args = request.params.arguments as {
owner: string;
repo: string;
title: string;
body?: string;
head: string;
base: string;
maintainer_can_modify?: boolean;
draft?: boolean;
};
const { owner, repo, ...options } = args;
const pullRequest = await createPullRequest(owner, repo, options);
return { toolResult: pullRequest };
}
throw new Error("Tool not found");
});
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("GitHub MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});

332
src/github/interfaces.ts Normal file
View File

@@ -0,0 +1,332 @@
// GitHub API Response Types
export interface GitHubErrorResponse {
message: string;
documentation_url?: string;
}
export interface GitHubFileContent {
type: string;
encoding: string;
size: number;
name: string;
path: string;
content: string;
sha: string;
url: string;
git_url: string;
html_url: string;
download_url: string;
}
export interface GitHubDirectoryContent {
type: string;
size: number;
name: string;
path: string;
sha: string;
url: string;
git_url: string;
html_url: string;
download_url: string | null;
}
export type GitHubContent = GitHubFileContent | GitHubDirectoryContent[];
export interface GitHubCreateUpdateFileResponse {
content: GitHubFileContent | null;
commit: {
sha: string;
node_id: string;
url: string;
html_url: string;
author: GitHubAuthor;
committer: GitHubAuthor;
message: string;
tree: {
sha: string;
url: string;
};
parents: Array<{
sha: string;
url: string;
html_url: string;
}>;
};
}
export interface GitHubAuthor {
name: string;
email: string;
date: string;
}
export interface GitHubTree {
sha: string;
url: string;
tree: Array<{
path: string;
mode: string;
type: string;
size?: number;
sha: string;
url: string;
}>;
truncated: boolean;
}
export interface GitHubCommit {
sha: string;
node_id: string;
url: string;
author: GitHubAuthor;
committer: GitHubAuthor;
message: string;
tree: {
sha: string;
url: string;
};
parents: Array<{
sha: string;
url: string;
}>;
}
export interface GitHubReference {
ref: string;
node_id: string;
url: string;
object: {
sha: string;
type: string;
url: string;
};
}
export interface GitHubRepository {
id: number;
node_id: string;
name: string;
full_name: string;
private: boolean;
owner: {
login: string;
id: number;
node_id: string;
avatar_url: string;
url: string;
html_url: string;
type: string;
};
html_url: string;
description: string | null;
fork: boolean;
url: string;
created_at: string;
updated_at: string;
pushed_at: string;
git_url: string;
ssh_url: string;
clone_url: string;
default_branch: string;
}
export interface GitHubSearchResponse {
total_count: number;
incomplete_results: boolean;
items: GitHubRepository[];
}
// Request Types
export interface CreateRepositoryOptions {
name?: string;
description?: string;
private?: boolean;
auto_init?: boolean;
}
export interface CreateTreeParams {
path: string;
mode: '100644' | '100755' | '040000' | '160000' | '120000';
type: 'blob' | 'tree' | 'commit';
content?: string;
sha?: string;
}
export interface FileOperation {
path: string;
content: string;
}
export interface GitHubIssue {
url: string;
repository_url: string;
labels_url: string;
comments_url: string;
events_url: string;
html_url: string;
id: number;
node_id: string;
number: number;
title: string;
user: {
login: string;
id: number;
avatar_url: string;
url: string;
html_url: string;
};
labels: Array<{
id: number;
node_id: string;
url: string;
name: string;
color: string;
default: boolean;
description?: string;
}>;
state: string;
locked: boolean;
assignee: null | {
login: string;
id: number;
avatar_url: string;
url: string;
html_url: string;
};
assignees: Array<{
login: string;
id: number;
avatar_url: string;
url: string;
html_url: string;
}>;
milestone: null | {
url: string;
html_url: string;
labels_url: string;
id: number;
node_id: string;
number: number;
title: string;
description: string;
state: string;
};
comments: number;
created_at: string;
updated_at: string;
closed_at: string | null;
body: string;
}
export interface CreateIssueOptions {
title: string;
body?: string;
assignees?: string[];
milestone?: number;
labels?: string[];
}
export interface GitHubPullRequest {
url: string;
id: number;
node_id: string;
html_url: string;
diff_url: string;
patch_url: string;
issue_url: string;
number: number;
state: string;
locked: boolean;
title: string;
user: {
login: string;
id: number;
avatar_url: string;
url: string;
html_url: string;
};
body: string;
created_at: string;
updated_at: string;
closed_at: string | null;
merged_at: string | null;
merge_commit_sha: string;
assignee: null | {
login: string;
id: number;
avatar_url: string;
url: string;
html_url: string;
};
assignees: Array<{
login: string;
id: number;
avatar_url: string;
url: string;
html_url: string;
}>;
head: {
label: string;
ref: string;
sha: string;
user: {
login: string;
id: number;
avatar_url: string;
url: string;
html_url: string;
};
repo: GitHubRepository;
};
base: {
label: string;
ref: string;
sha: string;
user: {
login: string;
id: number;
avatar_url: string;
url: string;
html_url: string;
};
repo: GitHubRepository;
};
}
export interface CreatePullRequestOptions {
title: string;
body?: string;
head: string;
base: string;
maintainer_can_modify?: boolean;
draft?: boolean;
}
export interface GitHubFork extends GitHubRepository {
// Fork specific fields
parent: {
name: string;
full_name: string;
owner: {
login: string;
id: number;
avatar_url: string;
};
html_url: string;
};
source: {
name: string;
full_name: string;
owner: {
login: string;
id: number;
avatar_url: string;
};
html_url: string;
};
}
export interface CreateBranchOptions {
ref: string; // The name for the new branch
sha: string; // The SHA of the commit to branch from
}

551
src/github/package-lock.json generated Normal file
View File

@@ -0,0 +1,551 @@
{
"name": "@modelcontextprotocol/server-github",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@modelcontextprotocol/server-github",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "0.6.0",
"@types/node-fetch": "^2.6.12",
"node-fetch": "^3.3.2"
},
"bin": {
"mcp-server-github": "dist/index.js"
},
"devDependencies": {
"shx": "^0.3.4",
"typescript": "^5.6.2"
}
},
"node_modules/@modelcontextprotocol/sdk": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.0.tgz",
"integrity": "sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==",
"dependencies": {
"content-type": "^1.0.5",
"raw-body": "^3.0.0",
"zod": "^3.23.8"
}
},
"node_modules/@types/node": {
"version": "22.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz",
"integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==",
"dependencies": {
"undici-types": "~6.19.8"
}
},
"node_modules/@types/node-fetch": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz",
"integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==",
"dependencies": {
"@types/node": "*",
"form-data": "^4.0.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"engines": {
"node": ">= 12"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/interpret": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true,
"engines": {
"node": ">= 0.10"
}
},
"node_modules/is-core-module": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
"integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
"dev": true,
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/raw-body": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.6.3",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
"integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
"dev": true,
"dependencies": {
"resolve": "^1.1.6"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dev": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/shelljs": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
"integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
"dev": true,
"dependencies": {
"glob": "^7.0.0",
"interpret": "^1.0.0",
"rechoir": "^0.6.2"
},
"bin": {
"shjs": "bin/shjs"
},
"engines": {
"node": ">=4"
}
},
"node_modules/shx": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz",
"integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==",
"dev": true,
"dependencies": {
"minimist": "^1.2.3",
"shelljs": "^0.8.5"
},
"bin": {
"shx": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/typescript": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"engines": {
"node": ">= 8"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"node_modules/zod": {
"version": "3.23.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

30
src/github/package.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "@modelcontextprotocol/server-github",
"version": "0.1.0",
"description": "MCP server for using the GitHub API",
"license": "MIT",
"author": "Anthropic, PBC (https://anthropic.com)",
"homepage": "https://modelcontextprotocol.io",
"bugs": "https://github.com/modelcontextprotocol/servers/issues",
"type": "module",
"bin": {
"mcp-server-github": "dist/index.js"
},
"files": [
"dist"
],
"scripts": {
"build": "tsc && shx chmod +x dist/*.js",
"prepare": "npm run build",
"watch": "tsc --watch"
},
"dependencies": {
"@modelcontextprotocol/sdk": "0.6.0",
"@types/node-fetch": "^2.6.12",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"shx": "^0.3.4",
"typescript": "^5.6.2"
}
}

11
src/github/tsconfig.json Normal file
View File

@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "."
},
"include": [
"./**/*.ts"
]
}