mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-18 00:03:23 +02:00
Merge branch 'main' into mahesh/add-filesystem
This commit is contained in:
56
package-lock.json
generated
56
package-lock.json
generated
@@ -179,6 +179,10 @@
|
||||
"resolved": "src/gdrive",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/server-github": {
|
||||
"resolved": "src/github",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/server-google-maps": {
|
||||
"resolved": "src/google-maps",
|
||||
"link": true
|
||||
@@ -3451,6 +3455,58 @@
|
||||
"typescript": "^5.6.2"
|
||||
}
|
||||
},
|
||||
"src/github": {
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"src/github/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"
|
||||
}
|
||||
},
|
||||
"src/github/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"
|
||||
}
|
||||
},
|
||||
"src/github/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"
|
||||
}
|
||||
},
|
||||
"src/google-maps": {
|
||||
"name": "@modelcontextprotocol/server-google-maps",
|
||||
"version": "0.1.0",
|
||||
|
||||
126
src/github/README.md
Normal file
126
src/github/README.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# GitHub MCP Server
|
||||
|
||||
MCP Server for the GitHub API, enabling file operations, repository management, and more.
|
||||
|
||||
### Features
|
||||
|
||||
- **Automatic Branch Creation**: When creating/updating files or pushing changes, branches are automatically created if they don't exist
|
||||
- **Comprehensive Error Handling**: Clear error messages for common issues
|
||||
- **Git History Preservation**: Operations maintain proper Git history without force pushing
|
||||
- **Batch Operations**: Support for both single-file and multi-file operations
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
1. `create_or_update_file`
|
||||
- Create or update a single file in a repository
|
||||
- Inputs:
|
||||
- `owner` (string): Repository owner (username or organization)
|
||||
- `repo` (string): Repository name
|
||||
- `path` (string): Path where to create/update the file
|
||||
- `content` (string): Content of the file
|
||||
- `message` (string): Commit message
|
||||
- `branch` (string): Branch to create/update the file in
|
||||
- `sha` (optional string): SHA of file being replaced (for updates)
|
||||
- Returns: File content and commit details
|
||||
|
||||
2. `push_files`
|
||||
- Push multiple files in a single commit
|
||||
- Inputs:
|
||||
- `owner` (string): Repository owner
|
||||
- `repo` (string): Repository name
|
||||
- `branch` (string): Branch to push to
|
||||
- `files` (array): Files to push, each with `path` and `content`
|
||||
- `message` (string): Commit message
|
||||
- Returns: Updated branch reference
|
||||
|
||||
3. `search_repositories`
|
||||
- Search for GitHub repositories
|
||||
- Inputs:
|
||||
- `query` (string): Search query
|
||||
- `page` (optional number): Page number for pagination
|
||||
- `perPage` (optional number): Results per page (max 100)
|
||||
- Returns: Repository search results
|
||||
|
||||
4. `create_repository`
|
||||
- Create a new GitHub repository
|
||||
- Inputs:
|
||||
- `name` (string): Repository name
|
||||
- `description` (optional string): Repository description
|
||||
- `private` (optional boolean): Whether repo should be private
|
||||
- `autoInit` (optional boolean): Initialize with README
|
||||
- Returns: Created repository details
|
||||
|
||||
5. `get_file_contents`
|
||||
- Get contents of a file or directory
|
||||
- Inputs:
|
||||
- `owner` (string): Repository owner
|
||||
- `repo` (string): Repository name
|
||||
- `path` (string): Path to file/directory
|
||||
- `branch` (optional string): Branch to get contents from
|
||||
- Returns: File/directory contents
|
||||
|
||||
6. `create_issue`
|
||||
- Create a new issue
|
||||
- Inputs:
|
||||
- `owner` (string): Repository owner
|
||||
- `repo` (string): Repository name
|
||||
- `title` (string): Issue title
|
||||
- `body` (optional string): Issue description
|
||||
- `assignees` (optional string[]): Usernames to assign
|
||||
- `labels` (optional string[]): Labels to add
|
||||
- `milestone` (optional number): Milestone number
|
||||
- Returns: Created issue details
|
||||
|
||||
7. `create_pull_request`
|
||||
- Create a new pull request
|
||||
- Inputs:
|
||||
- `owner` (string): Repository owner
|
||||
- `repo` (string): Repository name
|
||||
- `title` (string): PR title
|
||||
- `body` (optional string): PR description
|
||||
- `head` (string): Branch containing changes
|
||||
- `base` (string): Branch to merge into
|
||||
- `draft` (optional boolean): Create as draft PR
|
||||
- `maintainer_can_modify` (optional boolean): Allow maintainer edits
|
||||
- Returns: Created pull request details
|
||||
|
||||
8. `fork_repository`
|
||||
- Fork a repository
|
||||
- Inputs:
|
||||
- `owner` (string): Repository owner
|
||||
- `repo` (string): Repository name
|
||||
- `organization` (optional string): Organization to fork to
|
||||
- Returns: Forked repository details
|
||||
|
||||
9. `create_branch`
|
||||
- Create a new branch
|
||||
- Inputs:
|
||||
- `owner` (string): Repository owner
|
||||
- `repo` (string): Repository name
|
||||
- `branch` (string): Name for new branch
|
||||
- `from_branch` (optional string): Source branch (defaults to repo default)
|
||||
- Returns: Created branch reference
|
||||
|
||||
## Setup
|
||||
|
||||
### Personal Access Token
|
||||
[Create a GitHub Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with appropriate permissions:
|
||||
- Go to [Personal access tokens](https://github.com/settings/tokens) (in GitHub Settings > Developer settings)
|
||||
- Select which repositories you'd like this token to have access to (Public, All, or Select)
|
||||
- Create a token with the `repo` scope ("Full control of private repositories")
|
||||
- Alternatively, if working only with public repositories, select only the `public_repo` scope
|
||||
- Copy the generated token
|
||||
|
||||
### Usage with Claude Desktop
|
||||
To use this with Claude Desktop, add the following to your `claude_desktop_config.json`:
|
||||
```json
|
||||
{
|
||||
"mcp-server-github": {
|
||||
"command": "mcp-server-github",
|
||||
"env": {
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
646
src/github/index.ts
Normal file
646
src/github/index.ts
Normal file
@@ -0,0 +1,646 @@
|
||||
#!/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 {
|
||||
GitHubForkSchema,
|
||||
GitHubReferenceSchema,
|
||||
GitHubRepositorySchema,
|
||||
GitHubIssueSchema,
|
||||
GitHubPullRequestSchema,
|
||||
GitHubContentSchema,
|
||||
GitHubCreateUpdateFileResponseSchema,
|
||||
GitHubSearchResponseSchema,
|
||||
GitHubTreeSchema,
|
||||
GitHubCommitSchema,
|
||||
CreateRepositoryOptionsSchema,
|
||||
CreateIssueOptionsSchema,
|
||||
CreatePullRequestOptionsSchema,
|
||||
CreateBranchOptionsSchema,
|
||||
type GitHubFork,
|
||||
type GitHubReference,
|
||||
type GitHubRepository,
|
||||
type GitHubIssue,
|
||||
type GitHubPullRequest,
|
||||
type GitHubContent,
|
||||
type GitHubCreateUpdateFileResponse,
|
||||
type GitHubSearchResponse,
|
||||
type GitHubTree,
|
||||
type GitHubCommit,
|
||||
type FileOperation,
|
||||
CreateOrUpdateFileSchema,
|
||||
SearchRepositoriesSchema,
|
||||
CreateRepositorySchema,
|
||||
GetFileContentsSchema,
|
||||
PushFilesSchema,
|
||||
CreateIssueSchema,
|
||||
CreatePullRequestSchema,
|
||||
ForkRepositorySchema,
|
||||
CreateBranchSchema
|
||||
} from './schemas.js';
|
||||
import { z } from 'zod';
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema';
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 GitHubForkSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
async function createBranch(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options: z.infer<typeof CreateBranchOptionsSchema>
|
||||
): Promise<GitHubReference> {
|
||||
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 GitHubReferenceSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
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 (!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 = GitHubReferenceSchema.parse(await masterResponse.json());
|
||||
return data.object.sha;
|
||||
}
|
||||
|
||||
const data = GitHubReferenceSchema.parse(await response.json());
|
||||
return data.object.sha;
|
||||
}
|
||||
|
||||
async function getFileContents(
|
||||
owner: string,
|
||||
repo: string,
|
||||
path: string,
|
||||
branch?: string
|
||||
): Promise<GitHubContent> {
|
||||
let url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;
|
||||
if (branch) {
|
||||
url += `?ref=${branch}`;
|
||||
}
|
||||
|
||||
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 = GitHubContentSchema.parse(await response.json());
|
||||
|
||||
// If it's a file, decode the content
|
||||
if (!Array.isArray(data) && data.content) {
|
||||
data.content = Buffer.from(data.content, 'base64').toString('utf8');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async function createIssue(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options: z.infer<typeof CreateIssueOptionsSchema>
|
||||
): 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 GitHubIssueSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
async function createPullRequest(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options: z.infer<typeof CreatePullRequestOptionsSchema>
|
||||
): 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 GitHubPullRequestSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
async function createOrUpdateFile(
|
||||
owner: string,
|
||||
repo: string,
|
||||
path: string,
|
||||
content: string,
|
||||
message: string,
|
||||
branch: string,
|
||||
sha?: string
|
||||
): Promise<GitHubCreateUpdateFileResponse> {
|
||||
const encodedContent = Buffer.from(content).toString('base64');
|
||||
|
||||
let currentSha = sha;
|
||||
if (!currentSha) {
|
||||
try {
|
||||
const existingFile = await getFileContents(owner, repo, path, branch);
|
||||
if (!Array.isArray(existingFile)) {
|
||||
currentSha = existingFile.sha;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Note: File does not exist in branch, will create new file');
|
||||
}
|
||||
}
|
||||
|
||||
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;
|
||||
|
||||
const body = {
|
||||
message,
|
||||
content: encodedContent,
|
||||
branch,
|
||||
...(currentSha ? { sha: currentSha } : {})
|
||||
};
|
||||
|
||||
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 GitHubCreateUpdateFileResponseSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
async function createTree(
|
||||
owner: string,
|
||||
repo: string,
|
||||
files: FileOperation[],
|
||||
baseTree?: string
|
||||
): Promise<GitHubTree> {
|
||||
const tree = files.map(file => ({
|
||||
path: file.path,
|
||||
mode: '100644' as const,
|
||||
type: 'blob' as const,
|
||||
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 GitHubTreeSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
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 GitHubCommitSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
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 GitHubReferenceSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
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 = GitHubReferenceSchema.parse(await refResponse.json());
|
||||
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 GitHubSearchResponseSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
async function createRepository(
|
||||
options: z.infer<typeof CreateRepositoryOptionsSchema>
|
||||
): 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 GitHubRepositorySchema.parse(await response.json());
|
||||
}
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return {
|
||||
tools: [
|
||||
{
|
||||
name: "create_or_update_file",
|
||||
description: "Create or update a single file in a GitHub repository",
|
||||
inputSchema: zodToJsonSchema(CreateOrUpdateFileSchema)
|
||||
},
|
||||
{
|
||||
name: "search_repositories",
|
||||
description: "Search for GitHub repositories",
|
||||
inputSchema: zodToJsonSchema(SearchRepositoriesSchema)
|
||||
},
|
||||
{
|
||||
name: "create_repository",
|
||||
description: "Create a new GitHub repository in your account",
|
||||
inputSchema: zodToJsonSchema(CreateRepositorySchema)
|
||||
},
|
||||
{
|
||||
name: "get_file_contents",
|
||||
description: "Get the contents of a file or directory from a GitHub repository",
|
||||
inputSchema: zodToJsonSchema(GetFileContentsSchema)
|
||||
},
|
||||
{
|
||||
name: "push_files",
|
||||
description: "Push multiple files to a GitHub repository in a single commit",
|
||||
inputSchema: zodToJsonSchema(PushFilesSchema)
|
||||
},
|
||||
{
|
||||
name: "create_issue",
|
||||
description: "Create a new issue in a GitHub repository",
|
||||
inputSchema: zodToJsonSchema(CreateIssueSchema)
|
||||
},
|
||||
{
|
||||
name: "create_pull_request",
|
||||
description: "Create a new pull request in a GitHub repository",
|
||||
inputSchema: zodToJsonSchema(CreatePullRequestSchema)
|
||||
},
|
||||
{
|
||||
name: "fork_repository",
|
||||
description: "Fork a GitHub repository to your account or specified organization",
|
||||
inputSchema: zodToJsonSchema(ForkRepositorySchema)
|
||||
},
|
||||
{
|
||||
name: "create_branch",
|
||||
description: "Create a new branch in a GitHub repository",
|
||||
inputSchema: zodToJsonSchema(CreateBranchSchema)
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
try {
|
||||
if (!request.params.arguments) {
|
||||
throw new Error("Arguments are required");
|
||||
}
|
||||
|
||||
switch (request.params.name) {
|
||||
case "fork_repository": {
|
||||
const args = ForkRepositorySchema.parse(request.params.arguments);
|
||||
const fork = await forkRepository(args.owner, args.repo, args.organization);
|
||||
return { toolResult: fork };
|
||||
}
|
||||
|
||||
case "create_branch": {
|
||||
const args = CreateBranchSchema.parse(request.params.arguments);
|
||||
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 = GitHubReferenceSchema.parse(await response.json());
|
||||
sha = data.object.sha;
|
||||
} else {
|
||||
sha = await getDefaultBranchSHA(args.owner, args.repo);
|
||||
}
|
||||
|
||||
const branch = await createBranch(args.owner, args.repo, {
|
||||
ref: args.branch,
|
||||
sha
|
||||
});
|
||||
|
||||
return { toolResult: branch };
|
||||
}
|
||||
|
||||
case "search_repositories": {
|
||||
const args = SearchRepositoriesSchema.parse(request.params.arguments);
|
||||
const results = await searchRepositories(args.query, args.page, args.perPage);
|
||||
return { toolResult: results };
|
||||
}
|
||||
|
||||
case "create_repository": {
|
||||
const args = CreateRepositorySchema.parse(request.params.arguments);
|
||||
const repository = await createRepository(args);
|
||||
return { toolResult: repository };
|
||||
}
|
||||
|
||||
case "get_file_contents": {
|
||||
const args = GetFileContentsSchema.parse(request.params.arguments);
|
||||
const contents = await getFileContents(args.owner, args.repo, args.path, args.branch);
|
||||
return { toolResult: contents };
|
||||
}
|
||||
|
||||
case "create_or_update_file": {
|
||||
const args = CreateOrUpdateFileSchema.parse(request.params.arguments);
|
||||
const result = await createOrUpdateFile(
|
||||
args.owner,
|
||||
args.repo,
|
||||
args.path,
|
||||
args.content,
|
||||
args.message,
|
||||
args.branch,
|
||||
args.sha
|
||||
);
|
||||
return { toolResult: result };
|
||||
}
|
||||
|
||||
case "push_files": {
|
||||
const args = PushFilesSchema.parse(request.params.arguments);
|
||||
const result = await pushFiles(
|
||||
args.owner,
|
||||
args.repo,
|
||||
args.branch,
|
||||
args.files,
|
||||
args.message
|
||||
);
|
||||
return { toolResult: result };
|
||||
}
|
||||
|
||||
case "create_issue": {
|
||||
const args = CreateIssueSchema.parse(request.params.arguments);
|
||||
const { owner, repo, ...options } = args;
|
||||
const issue = await createIssue(owner, repo, options);
|
||||
return { toolResult: issue };
|
||||
}
|
||||
|
||||
case "create_pull_request": {
|
||||
const args = CreatePullRequestSchema.parse(request.params.arguments);
|
||||
const { owner, repo, ...options } = args;
|
||||
const pullRequest = await createPullRequest(owner, repo, options);
|
||||
return { toolResult: pullRequest };
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown tool: ${request.params.name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
throw new Error(`Invalid arguments: ${error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
551
src/github/package-lock.json
generated
Normal file
551
src/github/package-lock.json
generated
Normal 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
30
src/github/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
378
src/github/schemas.ts
Normal file
378
src/github/schemas.ts
Normal file
@@ -0,0 +1,378 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// Base schemas for common types
|
||||
export const GitHubAuthorSchema = z.object({
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
date: z.string()
|
||||
});
|
||||
|
||||
// Repository related schemas
|
||||
export const GitHubOwnerSchema = z.object({
|
||||
login: z.string(),
|
||||
id: z.number(),
|
||||
node_id: z.string(),
|
||||
avatar_url: z.string(),
|
||||
url: z.string(),
|
||||
html_url: z.string(),
|
||||
type: z.string()
|
||||
});
|
||||
|
||||
export const GitHubRepositorySchema = z.object({
|
||||
id: z.number(),
|
||||
node_id: z.string(),
|
||||
name: z.string(),
|
||||
full_name: z.string(),
|
||||
private: z.boolean(),
|
||||
owner: GitHubOwnerSchema,
|
||||
html_url: z.string(),
|
||||
description: z.string().nullable(),
|
||||
fork: z.boolean(),
|
||||
url: z.string(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
pushed_at: z.string(),
|
||||
git_url: z.string(),
|
||||
ssh_url: z.string(),
|
||||
clone_url: z.string(),
|
||||
default_branch: z.string()
|
||||
});
|
||||
|
||||
// File content schemas
|
||||
export const GitHubFileContentSchema = z.object({
|
||||
type: z.string(),
|
||||
encoding: z.string(),
|
||||
size: z.number(),
|
||||
name: z.string(),
|
||||
path: z.string(),
|
||||
content: z.string(),
|
||||
sha: z.string(),
|
||||
url: z.string(),
|
||||
git_url: z.string(),
|
||||
html_url: z.string(),
|
||||
download_url: z.string()
|
||||
});
|
||||
|
||||
export const GitHubDirectoryContentSchema = z.object({
|
||||
type: z.string(),
|
||||
size: z.number(),
|
||||
name: z.string(),
|
||||
path: z.string(),
|
||||
sha: z.string(),
|
||||
url: z.string(),
|
||||
git_url: z.string(),
|
||||
html_url: z.string(),
|
||||
download_url: z.string().nullable()
|
||||
});
|
||||
|
||||
export const GitHubContentSchema = z.union([
|
||||
GitHubFileContentSchema,
|
||||
z.array(GitHubDirectoryContentSchema)
|
||||
]);
|
||||
|
||||
// Operation schemas
|
||||
export const FileOperationSchema = z.object({
|
||||
path: z.string(),
|
||||
content: z.string()
|
||||
});
|
||||
|
||||
// Tree and commit schemas
|
||||
export const GitHubTreeEntrySchema = z.object({
|
||||
path: z.string(),
|
||||
mode: z.enum(['100644', '100755', '040000', '160000', '120000']),
|
||||
type: z.enum(['blob', 'tree', 'commit']),
|
||||
size: z.number().optional(),
|
||||
sha: z.string(),
|
||||
url: z.string()
|
||||
});
|
||||
|
||||
export const GitHubTreeSchema = z.object({
|
||||
sha: z.string(),
|
||||
url: z.string(),
|
||||
tree: z.array(GitHubTreeEntrySchema),
|
||||
truncated: z.boolean()
|
||||
});
|
||||
|
||||
export const GitHubCommitSchema = z.object({
|
||||
sha: z.string(),
|
||||
node_id: z.string(),
|
||||
url: z.string(),
|
||||
author: GitHubAuthorSchema,
|
||||
committer: GitHubAuthorSchema,
|
||||
message: z.string(),
|
||||
tree: z.object({
|
||||
sha: z.string(),
|
||||
url: z.string()
|
||||
}),
|
||||
parents: z.array(z.object({
|
||||
sha: z.string(),
|
||||
url: z.string()
|
||||
}))
|
||||
});
|
||||
|
||||
// Reference schema
|
||||
export const GitHubReferenceSchema = z.object({
|
||||
ref: z.string(),
|
||||
node_id: z.string(),
|
||||
url: z.string(),
|
||||
object: z.object({
|
||||
sha: z.string(),
|
||||
type: z.string(),
|
||||
url: z.string()
|
||||
})
|
||||
});
|
||||
|
||||
// Input schemas for operations
|
||||
export const CreateRepositoryOptionsSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
private: z.boolean().optional(),
|
||||
auto_init: z.boolean().optional()
|
||||
});
|
||||
|
||||
export const CreateIssueOptionsSchema = z.object({
|
||||
title: z.string(),
|
||||
body: z.string().optional(),
|
||||
assignees: z.array(z.string()).optional(),
|
||||
milestone: z.number().optional(),
|
||||
labels: z.array(z.string()).optional()
|
||||
});
|
||||
|
||||
export const CreatePullRequestOptionsSchema = z.object({
|
||||
title: z.string(),
|
||||
body: z.string().optional(),
|
||||
head: z.string(),
|
||||
base: z.string(),
|
||||
maintainer_can_modify: z.boolean().optional(),
|
||||
draft: z.boolean().optional()
|
||||
});
|
||||
|
||||
export const CreateBranchOptionsSchema = z.object({
|
||||
ref: z.string(),
|
||||
sha: z.string()
|
||||
});
|
||||
|
||||
// Response schemas for operations
|
||||
export const GitHubCreateUpdateFileResponseSchema = z.object({
|
||||
content: GitHubFileContentSchema.nullable(),
|
||||
commit: z.object({
|
||||
sha: z.string(),
|
||||
node_id: z.string(),
|
||||
url: z.string(),
|
||||
html_url: z.string(),
|
||||
author: GitHubAuthorSchema,
|
||||
committer: GitHubAuthorSchema,
|
||||
message: z.string(),
|
||||
tree: z.object({
|
||||
sha: z.string(),
|
||||
url: z.string()
|
||||
}),
|
||||
parents: z.array(z.object({
|
||||
sha: z.string(),
|
||||
url: z.string(),
|
||||
html_url: z.string()
|
||||
}))
|
||||
})
|
||||
});
|
||||
|
||||
export const GitHubSearchResponseSchema = z.object({
|
||||
total_count: z.number(),
|
||||
incomplete_results: z.boolean(),
|
||||
items: z.array(GitHubRepositorySchema)
|
||||
});
|
||||
|
||||
// Fork related schemas
|
||||
export const GitHubForkParentSchema = z.object({
|
||||
name: z.string(),
|
||||
full_name: z.string(),
|
||||
owner: z.object({
|
||||
login: z.string(),
|
||||
id: z.number(),
|
||||
avatar_url: z.string()
|
||||
}),
|
||||
html_url: z.string()
|
||||
});
|
||||
|
||||
export const GitHubForkSchema = GitHubRepositorySchema.extend({
|
||||
parent: GitHubForkParentSchema,
|
||||
source: GitHubForkParentSchema
|
||||
});
|
||||
|
||||
// Issue related schemas
|
||||
export const GitHubLabelSchema = z.object({
|
||||
id: z.number(),
|
||||
node_id: z.string(),
|
||||
url: z.string(),
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
default: z.boolean(),
|
||||
description: z.string().optional()
|
||||
});
|
||||
|
||||
export const GitHubIssueAssigneeSchema = z.object({
|
||||
login: z.string(),
|
||||
id: z.number(),
|
||||
avatar_url: z.string(),
|
||||
url: z.string(),
|
||||
html_url: z.string()
|
||||
});
|
||||
|
||||
export const GitHubMilestoneSchema = z.object({
|
||||
url: z.string(),
|
||||
html_url: z.string(),
|
||||
labels_url: z.string(),
|
||||
id: z.number(),
|
||||
node_id: z.string(),
|
||||
number: z.number(),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
state: z.string()
|
||||
});
|
||||
|
||||
export const GitHubIssueSchema = z.object({
|
||||
url: z.string(),
|
||||
repository_url: z.string(),
|
||||
labels_url: z.string(),
|
||||
comments_url: z.string(),
|
||||
events_url: z.string(),
|
||||
html_url: z.string(),
|
||||
id: z.number(),
|
||||
node_id: z.string(),
|
||||
number: z.number(),
|
||||
title: z.string(),
|
||||
user: GitHubIssueAssigneeSchema,
|
||||
labels: z.array(GitHubLabelSchema),
|
||||
state: z.string(),
|
||||
locked: z.boolean(),
|
||||
assignee: GitHubIssueAssigneeSchema.nullable(),
|
||||
assignees: z.array(GitHubIssueAssigneeSchema),
|
||||
milestone: GitHubMilestoneSchema.nullable(),
|
||||
comments: z.number(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
closed_at: z.string().nullable(),
|
||||
body: z.string()
|
||||
});
|
||||
|
||||
// Pull Request related schemas
|
||||
export const GitHubPullRequestHeadSchema = z.object({
|
||||
label: z.string(),
|
||||
ref: z.string(),
|
||||
sha: z.string(),
|
||||
user: GitHubIssueAssigneeSchema,
|
||||
repo: GitHubRepositorySchema
|
||||
});
|
||||
|
||||
export const GitHubPullRequestSchema = z.object({
|
||||
url: z.string(),
|
||||
id: z.number(),
|
||||
node_id: z.string(),
|
||||
html_url: z.string(),
|
||||
diff_url: z.string(),
|
||||
patch_url: z.string(),
|
||||
issue_url: z.string(),
|
||||
number: z.number(),
|
||||
state: z.string(),
|
||||
locked: z.boolean(),
|
||||
title: z.string(),
|
||||
user: GitHubIssueAssigneeSchema,
|
||||
body: z.string(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
closed_at: z.string().nullable(),
|
||||
merged_at: z.string().nullable(),
|
||||
merge_commit_sha: z.string(),
|
||||
assignee: GitHubIssueAssigneeSchema.nullable(),
|
||||
assignees: z.array(GitHubIssueAssigneeSchema),
|
||||
head: GitHubPullRequestHeadSchema,
|
||||
base: GitHubPullRequestHeadSchema
|
||||
});
|
||||
|
||||
const RepoParamsSchema = z.object({
|
||||
owner: z.string().describe("Repository owner (username or organization)"),
|
||||
repo: z.string().describe("Repository name")
|
||||
});
|
||||
|
||||
export const CreateOrUpdateFileSchema = RepoParamsSchema.extend({
|
||||
path: z.string().describe("Path where to create/update the file"),
|
||||
content: z.string().describe("Content of the file"),
|
||||
message: z.string().describe("Commit message"),
|
||||
branch: z.string().describe("Branch to create/update the file in"),
|
||||
sha: z.string().optional()
|
||||
.describe("SHA of the file being replaced (required when updating existing files)")
|
||||
});
|
||||
|
||||
export const SearchRepositoriesSchema = z.object({
|
||||
query: z.string().describe("Search query (see GitHub search syntax)"),
|
||||
page: z.number().optional().describe("Page number for pagination (default: 1)"),
|
||||
perPage: z.number().optional().describe("Number of results per page (default: 30, max: 100)")
|
||||
});
|
||||
|
||||
export const CreateRepositorySchema = z.object({
|
||||
name: z.string().describe("Repository name"),
|
||||
description: z.string().optional().describe("Repository description"),
|
||||
private: z.boolean().optional().describe("Whether the repository should be private"),
|
||||
autoInit: z.boolean().optional().describe("Initialize with README.md")
|
||||
});
|
||||
|
||||
export const GetFileContentsSchema = RepoParamsSchema.extend({
|
||||
path: z.string().describe("Path to the file or directory"),
|
||||
branch: z.string().optional().describe("Branch to get contents from")
|
||||
});
|
||||
|
||||
export const PushFilesSchema = RepoParamsSchema.extend({
|
||||
branch: z.string().describe("Branch to push to (e.g., 'main' or 'master')"),
|
||||
files: z.array(z.object({
|
||||
path: z.string().describe("Path where to create the file"),
|
||||
content: z.string().describe("Content of the file")
|
||||
})).describe("Array of files to push"),
|
||||
message: z.string().describe("Commit message")
|
||||
});
|
||||
|
||||
export const CreateIssueSchema = RepoParamsSchema.extend({
|
||||
title: z.string().describe("Issue title"),
|
||||
body: z.string().optional().describe("Issue body/description"),
|
||||
assignees: z.array(z.string()).optional().describe("Array of usernames to assign"),
|
||||
labels: z.array(z.string()).optional().describe("Array of label names"),
|
||||
milestone: z.number().optional().describe("Milestone number to assign")
|
||||
});
|
||||
|
||||
export const CreatePullRequestSchema = RepoParamsSchema.extend({
|
||||
title: z.string().describe("Pull request title"),
|
||||
body: z.string().optional().describe("Pull request body/description"),
|
||||
head: z.string().describe("The name of the branch where your changes are implemented"),
|
||||
base: z.string().describe("The name of the branch you want the changes pulled into"),
|
||||
draft: z.boolean().optional().describe("Whether to create the pull request as a draft"),
|
||||
maintainer_can_modify: z.boolean().optional()
|
||||
.describe("Whether maintainers can modify the pull request")
|
||||
});
|
||||
|
||||
export const ForkRepositorySchema = RepoParamsSchema.extend({
|
||||
organization: z.string().optional()
|
||||
.describe("Optional: organization to fork to (defaults to your personal account)")
|
||||
});
|
||||
|
||||
export const CreateBranchSchema = RepoParamsSchema.extend({
|
||||
branch: z.string().describe("Name for the new branch"),
|
||||
from_branch: z.string().optional()
|
||||
.describe("Optional: source branch to create from (defaults to the repository's default branch)")
|
||||
});
|
||||
|
||||
// Export types
|
||||
export type GitHubAuthor = z.infer<typeof GitHubAuthorSchema>;
|
||||
export type GitHubFork = z.infer<typeof GitHubForkSchema>;
|
||||
export type GitHubIssue = z.infer<typeof GitHubIssueSchema>;
|
||||
export type GitHubPullRequest = z.infer<typeof GitHubPullRequestSchema>;export type GitHubRepository = z.infer<typeof GitHubRepositorySchema>;
|
||||
export type GitHubFileContent = z.infer<typeof GitHubFileContentSchema>;
|
||||
export type GitHubDirectoryContent = z.infer<typeof GitHubDirectoryContentSchema>;
|
||||
export type GitHubContent = z.infer<typeof GitHubContentSchema>;
|
||||
export type FileOperation = z.infer<typeof FileOperationSchema>;
|
||||
export type GitHubTree = z.infer<typeof GitHubTreeSchema>;
|
||||
export type GitHubCommit = z.infer<typeof GitHubCommitSchema>;
|
||||
export type GitHubReference = z.infer<typeof GitHubReferenceSchema>;
|
||||
export type CreateRepositoryOptions = z.infer<typeof CreateRepositoryOptionsSchema>;
|
||||
export type CreateIssueOptions = z.infer<typeof CreateIssueOptionsSchema>;
|
||||
export type CreatePullRequestOptions = z.infer<typeof CreatePullRequestOptionsSchema>;
|
||||
export type CreateBranchOptions = z.infer<typeof CreateBranchOptionsSchema>;
|
||||
export type GitHubCreateUpdateFileResponse = z.infer<typeof GitHubCreateUpdateFileResponseSchema>;
|
||||
export type GitHubSearchResponse = z.infer<typeof GitHubSearchResponseSchema>;
|
||||
11
src/github/tsconfig.json
Normal file
11
src/github/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user