From 9be6268005db9b0254071438fc93cecb40c16103 Mon Sep 17 00:00:00 2001 From: Elie Schoppik Date: Wed, 20 Nov 2024 13:08:06 -0500 Subject: [PATCH] init on github --- src/github/index.ts | 953 +++++++++++++++++++++++++++++++++++ src/github/interfaces.ts | 332 ++++++++++++ src/github/package-lock.json | 551 ++++++++++++++++++++ src/github/package.json | 30 ++ src/github/tsconfig.json | 11 + 5 files changed, 1877 insertions(+) create mode 100644 src/github/index.ts create mode 100644 src/github/interfaces.ts create mode 100644 src/github/package-lock.json create mode 100644 src/github/package.json create mode 100644 src/github/tsconfig.json diff --git a/src/github/index.ts b/src/github/index.ts new file mode 100644 index 00000000..784577b6 --- /dev/null +++ b/src/github/index.ts @@ -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 { + 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 { + // 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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); +}); \ No newline at end of file diff --git a/src/github/interfaces.ts b/src/github/interfaces.ts new file mode 100644 index 00000000..ce3518e6 --- /dev/null +++ b/src/github/interfaces.ts @@ -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 + } \ No newline at end of file diff --git a/src/github/package-lock.json b/src/github/package-lock.json new file mode 100644 index 00000000..3f8a6b14 --- /dev/null +++ b/src/github/package-lock.json @@ -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" + } + } + } +} diff --git a/src/github/package.json b/src/github/package.json new file mode 100644 index 00000000..11fe17d6 --- /dev/null +++ b/src/github/package.json @@ -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" + } +} diff --git a/src/github/tsconfig.json b/src/github/tsconfig.json new file mode 100644 index 00000000..4d33cae1 --- /dev/null +++ b/src/github/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "." + }, + "include": [ + "./**/*.ts" + ] + } + \ No newline at end of file