Github MCP Server

  • src
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import type { Tool } from "@modelcontextprotocol/sdk/types.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { SEARCH_REPOSITORIES_TOOL, searchRepositories, searchRepositoriesInputSchema, } from "./tools/search-repositories.js"; import { VERSION } from "./constants.js"; import { GET_ISSUE_TOOL, getIssue, getIssueInputSchema, } from "./tools/get-issue.ts"; import { GET_PULL_REQUEST_TOOL, getPullRequest, getPullRequestInputSchema, } from "./tools/get-pull-request.ts"; import { LIST_REPOSITORIES_ISSUES_TOOL, listRepositoriesIssues, listRepositoriesIssuesInputSchema, } from "./tools/list-repositories-issues.ts"; import { LIST_REPOSITORIES_PULL_REQUESTS_TOOL, listRepositoriesPullRequests, listRepositoriesPullRequestsInputSchema, } from "./tools/list-repositories-pull-requests.ts"; import { SEARCH_ISSUES_TOOL, searchIssues, searchIssuesInputSchema, } from "./tools/search-issues.ts"; import { SEARCH_CODE_TOOL, searchCode, searchCodeInputSchema, } from "./tools/search-code.ts"; import { SEARCH_USERS_TOOL, searchUsers, searchUsersInputSchema, } from "./tools/search-users.ts"; import { SEARCH_COMMITS_TOOL, searchCommits, searchCommitsInputSchema, } from "./tools/search-commits.ts"; import { SEARCH_TOPICS_TOOL, searchTopics, searchTopicsInputSchema, } from "./tools/search-topics.ts"; import { SEARCH_LABELS_TOOL, searchLabels, searchLabelsInputSchema, } from "./tools/search-labels.ts"; const server = new Server( { name: "Github MCP Server", version: VERSION }, { capabilities: { tools: {} } }, ); export const tools = [ // search SEARCH_REPOSITORIES_TOOL, SEARCH_ISSUES_TOOL, SEARCH_CODE_TOOL, SEARCH_USERS_TOOL, SEARCH_COMMITS_TOOL, SEARCH_TOPICS_TOOL, SEARCH_LABELS_TOOL, // issues GET_ISSUE_TOOL, LIST_REPOSITORIES_ISSUES_TOOL, // pull requests LIST_REPOSITORIES_PULL_REQUESTS_TOOL, GET_PULL_REQUEST_TOOL, ] satisfies Tool[]; server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const name = request.params.name; const args = request.params.arguments; if (!args) throw new Error("No arguments provided"); if (name === SEARCH_REPOSITORIES_TOOL.name) { const input = searchRepositoriesInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await searchRepositories(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === GET_ISSUE_TOOL.name) { const input = getIssueInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await getIssue(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === GET_PULL_REQUEST_TOOL.name) { const input = getPullRequestInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await getPullRequest(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === LIST_REPOSITORIES_ISSUES_TOOL.name) { const input = listRepositoriesIssuesInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await listRepositoriesIssues(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === LIST_REPOSITORIES_PULL_REQUESTS_TOOL.name) { const input = listRepositoriesPullRequestsInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await listRepositoriesPullRequests(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === SEARCH_ISSUES_TOOL.name) { const input = searchIssuesInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await searchIssues(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === SEARCH_CODE_TOOL.name) { const input = searchCodeInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await searchCode(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === SEARCH_USERS_TOOL.name) { const input = searchUsersInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await searchUsers(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === SEARCH_COMMITS_TOOL.name) { const input = searchCommitsInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await searchCommits(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === SEARCH_TOPICS_TOOL.name) { const input = searchTopicsInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await searchTopics(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } if (name === SEARCH_LABELS_TOOL.name) { const input = searchLabelsInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [{ type: "text", text: "Invalid input" }], }; } const result = await searchLabels(input.data); if (result.isErr()) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } return { content: [ { type: "text", text: JSON.stringify(result.value, null, 2) }, ], }; } throw new Error(`Unknown tool: ${name}`); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { return { isError: true, content: [{ type: "text", text: "An error occurred" }], }; } }); async function run() { const transport = new StdioServerTransport(); await server.connect(transport); } run().catch((error) => { console.error("Fatal error in run()", error); process.exit(1); });