mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-18 08:03:26 +02:00
code for search in github
This commit is contained in:
@@ -41,19 +41,32 @@ import {
|
|||||||
CreateIssueSchema,
|
CreateIssueSchema,
|
||||||
CreatePullRequestSchema,
|
CreatePullRequestSchema,
|
||||||
ForkRepositorySchema,
|
ForkRepositorySchema,
|
||||||
CreateBranchSchema
|
CreateBranchSchema,
|
||||||
} from './schemas.js';
|
SearchCodeSchema,
|
||||||
import { z } from 'zod';
|
SearchIssuesSchema,
|
||||||
import { zodToJsonSchema } from 'zod-to-json-schema';
|
SearchUsersSchema,
|
||||||
|
SearchCodeResponseSchema,
|
||||||
|
SearchIssuesResponseSchema,
|
||||||
|
SearchUsersResponseSchema,
|
||||||
|
type SearchCodeResponse,
|
||||||
|
type SearchIssuesResponse,
|
||||||
|
type SearchUsersResponse,
|
||||||
|
} from "./schemas.js";
|
||||||
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
||||||
|
import { z } from "zod";
|
||||||
|
import type { CallToolRequest } from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
const server = new Server({
|
const server = new Server(
|
||||||
name: "github-mcp-server",
|
{
|
||||||
version: "0.1.0",
|
name: "github-mcp-server",
|
||||||
}, {
|
version: "0.1.0",
|
||||||
capabilities: {
|
},
|
||||||
tools: {}
|
{
|
||||||
|
capabilities: {
|
||||||
|
tools: {},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const GITHUB_PERSONAL_ACCESS_TOKEN = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
|
const GITHUB_PERSONAL_ACCESS_TOKEN = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
|
||||||
|
|
||||||
@@ -74,10 +87,10 @@ async function forkRepository(
|
|||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server"
|
"User-Agent": "github-mcp-server",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -99,15 +112,15 @@ async function createBranch(
|
|||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server",
|
"User-Agent": "github-mcp-server",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
ref: fullRef,
|
ref: fullRef,
|
||||||
sha: options.sha
|
sha: options.sha,
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -126,10 +139,10 @@ async function getDefaultBranchSHA(
|
|||||||
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/main`,
|
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/main`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server"
|
"User-Agent": "github-mcp-server",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -138,15 +151,17 @@ async function getDefaultBranchSHA(
|
|||||||
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/master`,
|
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/master`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server"
|
"User-Agent": "github-mcp-server",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!masterResponse.ok) {
|
if (!masterResponse.ok) {
|
||||||
throw new Error("Could not find default branch (tried 'main' and 'master')");
|
throw new Error(
|
||||||
|
"Could not find default branch (tried 'main' and 'master')"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = GitHubReferenceSchema.parse(await masterResponse.json());
|
const data = GitHubReferenceSchema.parse(await masterResponse.json());
|
||||||
@@ -170,10 +185,10 @@ async function getFileContents(
|
|||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server"
|
"User-Agent": "github-mcp-server",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -184,7 +199,7 @@ async function getFileContents(
|
|||||||
|
|
||||||
// If it's a file, decode the content
|
// If it's a file, decode the content
|
||||||
if (!Array.isArray(data) && data.content) {
|
if (!Array.isArray(data) && data.content) {
|
||||||
data.content = Buffer.from(data.content, 'base64').toString('utf8');
|
data.content = Buffer.from(data.content, "base64").toString("utf8");
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@@ -200,12 +215,12 @@ async function createIssue(
|
|||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server",
|
"User-Agent": "github-mcp-server",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(options)
|
body: JSON.stringify(options),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -226,12 +241,12 @@ async function createPullRequest(
|
|||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server",
|
"User-Agent": "github-mcp-server",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(options)
|
body: JSON.stringify(options),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -251,7 +266,7 @@ async function createOrUpdateFile(
|
|||||||
branch: string,
|
branch: string,
|
||||||
sha?: string
|
sha?: string
|
||||||
): Promise<GitHubCreateUpdateFileResponse> {
|
): Promise<GitHubCreateUpdateFileResponse> {
|
||||||
const encodedContent = Buffer.from(content).toString('base64');
|
const encodedContent = Buffer.from(content).toString("base64");
|
||||||
|
|
||||||
let currentSha = sha;
|
let currentSha = sha;
|
||||||
if (!currentSha) {
|
if (!currentSha) {
|
||||||
@@ -261,7 +276,9 @@ async function createOrUpdateFile(
|
|||||||
currentSha = existingFile.sha;
|
currentSha = existingFile.sha;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Note: File does not exist in branch, will create new file');
|
console.error(
|
||||||
|
"Note: File does not exist in branch, will create new file"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,18 +288,18 @@ async function createOrUpdateFile(
|
|||||||
message,
|
message,
|
||||||
content: encodedContent,
|
content: encodedContent,
|
||||||
branch,
|
branch,
|
||||||
...(currentSha ? { sha: currentSha } : {})
|
...(currentSha ? { sha: currentSha } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server",
|
"User-Agent": "github-mcp-server",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -298,11 +315,11 @@ async function createTree(
|
|||||||
files: FileOperation[],
|
files: FileOperation[],
|
||||||
baseTree?: string
|
baseTree?: string
|
||||||
): Promise<GitHubTree> {
|
): Promise<GitHubTree> {
|
||||||
const tree = files.map(file => ({
|
const tree = files.map((file) => ({
|
||||||
path: file.path,
|
path: file.path,
|
||||||
mode: '100644' as const,
|
mode: "100644" as const,
|
||||||
type: 'blob' as const,
|
type: "blob" as const,
|
||||||
content: file.content
|
content: file.content,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -310,15 +327,15 @@ async function createTree(
|
|||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server",
|
"User-Agent": "github-mcp-server",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
tree,
|
tree,
|
||||||
base_tree: baseTree
|
base_tree: baseTree,
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -341,16 +358,16 @@ async function createCommit(
|
|||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server",
|
"User-Agent": "github-mcp-server",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
message,
|
message,
|
||||||
tree,
|
tree,
|
||||||
parents
|
parents,
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -372,15 +389,15 @@ async function updateReference(
|
|||||||
{
|
{
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server",
|
"User-Agent": "github-mcp-server",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
sha,
|
sha,
|
||||||
force: true
|
force: true,
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -402,10 +419,10 @@ async function pushFiles(
|
|||||||
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
|
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server"
|
"User-Agent": "github-mcp-server",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -417,7 +434,9 @@ async function pushFiles(
|
|||||||
const commitSha = ref.object.sha;
|
const commitSha = ref.object.sha;
|
||||||
|
|
||||||
const tree = await createTree(owner, repo, files, commitSha);
|
const tree = await createTree(owner, repo, files, commitSha);
|
||||||
const commit = await createCommit(owner, repo, message, tree.sha, [commitSha]);
|
const commit = await createCommit(owner, repo, message, tree.sha, [
|
||||||
|
commitSha,
|
||||||
|
]);
|
||||||
return await updateReference(owner, repo, `heads/${branch}`, commit.sha);
|
return await updateReference(owner, repo, `heads/${branch}`, commit.sha);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,10 +452,10 @@ async function searchRepositories(
|
|||||||
|
|
||||||
const response = await fetch(url.toString(), {
|
const response = await fetch(url.toString(), {
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server"
|
"User-Agent": "github-mcp-server",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -452,12 +471,12 @@ async function createRepository(
|
|||||||
const response = await fetch("https://api.github.com/user/repos", {
|
const response = await fetch("https://api.github.com/user/repos", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
"Accept": "application/vnd.github.v3+json",
|
Accept: "application/vnd.github.v3+json",
|
||||||
"User-Agent": "github-mcp-server",
|
"User-Agent": "github-mcp-server",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(options)
|
body: JSON.stringify(options),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -467,172 +486,307 @@ async function createRepository(
|
|||||||
return GitHubRepositorySchema.parse(await response.json());
|
return GitHubRepositorySchema.parse(await response.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function searchCode(
|
||||||
|
params: z.infer<typeof SearchCodeSchema>
|
||||||
|
): Promise<SearchCodeResponse> {
|
||||||
|
const url = new URL("https://api.github.com/search/code");
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== null) {
|
||||||
|
url.searchParams.append(key, value.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 SearchCodeResponseSchema.parse(await response.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchIssues(
|
||||||
|
params: z.infer<typeof SearchIssuesSchema>
|
||||||
|
): Promise<SearchIssuesResponse> {
|
||||||
|
const url = new URL("https://api.github.com/search/issues");
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== null) {
|
||||||
|
url.searchParams.append(key, value.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 SearchIssuesResponseSchema.parse(await response.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchUsers(
|
||||||
|
params: z.infer<typeof SearchUsersSchema>
|
||||||
|
): Promise<SearchUsersResponse> {
|
||||||
|
const url = new URL("https://api.github.com/search/users");
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== null) {
|
||||||
|
url.searchParams.append(key, value.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 SearchUsersResponseSchema.parse(await response.json());
|
||||||
|
}
|
||||||
|
|
||||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||||
return {
|
return {
|
||||||
tools: [
|
tools: [
|
||||||
{
|
{
|
||||||
name: "create_or_update_file",
|
name: "create_or_update_file",
|
||||||
description: "Create or update a single file in a GitHub repository",
|
description: "Create or update a single file in a GitHub repository",
|
||||||
inputSchema: zodToJsonSchema(CreateOrUpdateFileSchema)
|
inputSchema: zodToJsonSchema(CreateOrUpdateFileSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "search_repositories",
|
name: "search_repositories",
|
||||||
description: "Search for GitHub repositories",
|
description: "Search for GitHub repositories",
|
||||||
inputSchema: zodToJsonSchema(SearchRepositoriesSchema)
|
inputSchema: zodToJsonSchema(SearchRepositoriesSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "create_repository",
|
name: "create_repository",
|
||||||
description: "Create a new GitHub repository in your account",
|
description: "Create a new GitHub repository in your account",
|
||||||
inputSchema: zodToJsonSchema(CreateRepositorySchema)
|
inputSchema: zodToJsonSchema(CreateRepositorySchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "get_file_contents",
|
name: "get_file_contents",
|
||||||
description: "Get the contents of a file or directory from a GitHub repository",
|
description:
|
||||||
inputSchema: zodToJsonSchema(GetFileContentsSchema)
|
"Get the contents of a file or directory from a GitHub repository",
|
||||||
|
inputSchema: zodToJsonSchema(GetFileContentsSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "push_files",
|
name: "push_files",
|
||||||
description: "Push multiple files to a GitHub repository in a single commit",
|
description:
|
||||||
inputSchema: zodToJsonSchema(PushFilesSchema)
|
"Push multiple files to a GitHub repository in a single commit",
|
||||||
|
inputSchema: zodToJsonSchema(PushFilesSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "create_issue",
|
name: "create_issue",
|
||||||
description: "Create a new issue in a GitHub repository",
|
description: "Create a new issue in a GitHub repository",
|
||||||
inputSchema: zodToJsonSchema(CreateIssueSchema)
|
inputSchema: zodToJsonSchema(CreateIssueSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "create_pull_request",
|
name: "create_pull_request",
|
||||||
description: "Create a new pull request in a GitHub repository",
|
description: "Create a new pull request in a GitHub repository",
|
||||||
inputSchema: zodToJsonSchema(CreatePullRequestSchema)
|
inputSchema: zodToJsonSchema(CreatePullRequestSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fork_repository",
|
name: "fork_repository",
|
||||||
description: "Fork a GitHub repository to your account or specified organization",
|
description:
|
||||||
inputSchema: zodToJsonSchema(ForkRepositorySchema)
|
"Fork a GitHub repository to your account or specified organization",
|
||||||
|
inputSchema: zodToJsonSchema(ForkRepositorySchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "create_branch",
|
name: "create_branch",
|
||||||
description: "Create a new branch in a GitHub repository",
|
description: "Create a new branch in a GitHub repository",
|
||||||
inputSchema: zodToJsonSchema(CreateBranchSchema)
|
inputSchema: zodToJsonSchema(CreateBranchSchema),
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
|
name: "search_code",
|
||||||
|
description: "Search for code across GitHub repositories",
|
||||||
|
inputSchema: zodToJsonSchema(SearchCodeSchema),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "search_issues",
|
||||||
|
description:
|
||||||
|
"Search for issues and pull requests across GitHub repositories",
|
||||||
|
inputSchema: zodToJsonSchema(SearchIssuesSchema),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "search_users",
|
||||||
|
description: "Search for users on GitHub",
|
||||||
|
inputSchema: zodToJsonSchema(SearchUsersSchema),
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
server.setRequestHandler(
|
||||||
try {
|
CallToolRequestSchema,
|
||||||
if (!request.params.arguments) {
|
async (request: CallToolRequest) => {
|
||||||
throw new Error("Arguments are required");
|
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": {
|
switch (request.params.name) {
|
||||||
const args = CreateBranchSchema.parse(request.params.arguments);
|
case "fork_repository": {
|
||||||
let sha: string;
|
const args = ForkRepositorySchema.parse(request.params.arguments);
|
||||||
if (args.from_branch) {
|
const fork = await forkRepository(
|
||||||
const response = await fetch(
|
args.owner,
|
||||||
`https://api.github.com/repos/${args.owner}/${args.repo}/git/refs/heads/${args.from_branch}`,
|
args.repo,
|
||||||
{
|
args.organization
|
||||||
headers: {
|
|
||||||
"Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
|
||||||
"Accept": "application/vnd.github.v3+json",
|
|
||||||
"User-Agent": "github-mcp-server"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
return { toolResult: fork };
|
||||||
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, {
|
case "create_branch": {
|
||||||
ref: args.branch,
|
const args = CreateBranchSchema.parse(request.params.arguments);
|
||||||
sha
|
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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { toolResult: branch };
|
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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
case "search_code": {
|
||||||
|
const args = SearchCodeSchema.parse(request.params.arguments);
|
||||||
|
const results = await searchCode(args);
|
||||||
|
return { toolResult: results };
|
||||||
|
}
|
||||||
|
|
||||||
|
case "search_issues": {
|
||||||
|
const args = SearchIssuesSchema.parse(request.params.arguments);
|
||||||
|
const results = await searchIssues(args);
|
||||||
|
return { toolResult: results };
|
||||||
|
}
|
||||||
|
|
||||||
|
case "search_users": {
|
||||||
|
const args = SearchUsersSchema.parse(request.params.arguments);
|
||||||
|
const results = await searchUsers(args);
|
||||||
|
return { toolResult: results };
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
case "search_repositories": {
|
if (error instanceof z.ZodError) {
|
||||||
const args = SearchRepositoriesSchema.parse(request.params.arguments);
|
throw new Error(
|
||||||
const results = await searchRepositories(args.query, args.page, args.perPage);
|
`Invalid arguments: ${error.errors
|
||||||
return { toolResult: results };
|
.map(
|
||||||
}
|
(e: z.ZodError["errors"][number]) =>
|
||||||
|
`${e.path.join(".")}: ${e.message}`
|
||||||
case "create_repository": {
|
)
|
||||||
const args = CreateRepositorySchema.parse(request.params.arguments);
|
.join(", ")}`
|
||||||
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 };
|
|
||||||
}
|
}
|
||||||
|
throw error;
|
||||||
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() {
|
async function runServer() {
|
||||||
const transport = new StdioServerTransport();
|
const transport = new StdioServerTransport();
|
||||||
|
|||||||
@@ -20,8 +20,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "0.6.0",
|
"@modelcontextprotocol/sdk": "0.6.0",
|
||||||
|
"@types/node": "^20.11.0",
|
||||||
"@types/node-fetch": "^2.6.12",
|
"@types/node-fetch": "^2.6.12",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
|
"zod": "^3.22.4",
|
||||||
"zod-to-json-schema": "^3.23.5"
|
"zod-to-json-schema": "^3.23.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
|
|
||||||
// Base schemas for common types
|
// Base schemas for common types
|
||||||
export const GitHubAuthorSchema = z.object({
|
export const GitHubAuthorSchema = z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
email: z.string(),
|
email: z.string(),
|
||||||
date: z.string()
|
date: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Repository related schemas
|
// Repository related schemas
|
||||||
@@ -15,7 +15,7 @@ export const GitHubOwnerSchema = z.object({
|
|||||||
avatar_url: z.string(),
|
avatar_url: z.string(),
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
html_url: z.string(),
|
html_url: z.string(),
|
||||||
type: z.string()
|
type: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubRepositorySchema = z.object({
|
export const GitHubRepositorySchema = z.object({
|
||||||
@@ -35,7 +35,7 @@ export const GitHubRepositorySchema = z.object({
|
|||||||
git_url: z.string(),
|
git_url: z.string(),
|
||||||
ssh_url: z.string(),
|
ssh_url: z.string(),
|
||||||
clone_url: z.string(),
|
clone_url: z.string(),
|
||||||
default_branch: z.string()
|
default_branch: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// File content schemas
|
// File content schemas
|
||||||
@@ -50,7 +50,7 @@ export const GitHubFileContentSchema = z.object({
|
|||||||
url: z.string(),
|
url: z.string(),
|
||||||
git_url: z.string(),
|
git_url: z.string(),
|
||||||
html_url: z.string(),
|
html_url: z.string(),
|
||||||
download_url: z.string()
|
download_url: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubDirectoryContentSchema = z.object({
|
export const GitHubDirectoryContentSchema = z.object({
|
||||||
@@ -62,35 +62,35 @@ export const GitHubDirectoryContentSchema = z.object({
|
|||||||
url: z.string(),
|
url: z.string(),
|
||||||
git_url: z.string(),
|
git_url: z.string(),
|
||||||
html_url: z.string(),
|
html_url: z.string(),
|
||||||
download_url: z.string().nullable()
|
download_url: z.string().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubContentSchema = z.union([
|
export const GitHubContentSchema = z.union([
|
||||||
GitHubFileContentSchema,
|
GitHubFileContentSchema,
|
||||||
z.array(GitHubDirectoryContentSchema)
|
z.array(GitHubDirectoryContentSchema),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Operation schemas
|
// Operation schemas
|
||||||
export const FileOperationSchema = z.object({
|
export const FileOperationSchema = z.object({
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
content: z.string()
|
content: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tree and commit schemas
|
// Tree and commit schemas
|
||||||
export const GitHubTreeEntrySchema = z.object({
|
export const GitHubTreeEntrySchema = z.object({
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
mode: z.enum(['100644', '100755', '040000', '160000', '120000']),
|
mode: z.enum(["100644", "100755", "040000", "160000", "120000"]),
|
||||||
type: z.enum(['blob', 'tree', 'commit']),
|
type: z.enum(["blob", "tree", "commit"]),
|
||||||
size: z.number().optional(),
|
size: z.number().optional(),
|
||||||
sha: z.string(),
|
sha: z.string(),
|
||||||
url: z.string()
|
url: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubTreeSchema = z.object({
|
export const GitHubTreeSchema = z.object({
|
||||||
sha: z.string(),
|
sha: z.string(),
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
tree: z.array(GitHubTreeEntrySchema),
|
tree: z.array(GitHubTreeEntrySchema),
|
||||||
truncated: z.boolean()
|
truncated: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubCommitSchema = z.object({
|
export const GitHubCommitSchema = z.object({
|
||||||
@@ -102,12 +102,14 @@ export const GitHubCommitSchema = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
tree: z.object({
|
tree: z.object({
|
||||||
sha: z.string(),
|
sha: z.string(),
|
||||||
url: z.string()
|
url: z.string(),
|
||||||
}),
|
}),
|
||||||
parents: z.array(z.object({
|
parents: z.array(
|
||||||
sha: z.string(),
|
z.object({
|
||||||
url: z.string()
|
sha: z.string(),
|
||||||
}))
|
url: z.string(),
|
||||||
|
})
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reference schema
|
// Reference schema
|
||||||
@@ -118,8 +120,8 @@ export const GitHubReferenceSchema = z.object({
|
|||||||
object: z.object({
|
object: z.object({
|
||||||
sha: z.string(),
|
sha: z.string(),
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
url: z.string()
|
url: z.string(),
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Input schemas for operations
|
// Input schemas for operations
|
||||||
@@ -127,7 +129,7 @@ export const CreateRepositoryOptionsSchema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
private: z.boolean().optional(),
|
private: z.boolean().optional(),
|
||||||
auto_init: z.boolean().optional()
|
auto_init: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CreateIssueOptionsSchema = z.object({
|
export const CreateIssueOptionsSchema = z.object({
|
||||||
@@ -135,7 +137,7 @@ export const CreateIssueOptionsSchema = z.object({
|
|||||||
body: z.string().optional(),
|
body: z.string().optional(),
|
||||||
assignees: z.array(z.string()).optional(),
|
assignees: z.array(z.string()).optional(),
|
||||||
milestone: z.number().optional(),
|
milestone: z.number().optional(),
|
||||||
labels: z.array(z.string()).optional()
|
labels: z.array(z.string()).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CreatePullRequestOptionsSchema = z.object({
|
export const CreatePullRequestOptionsSchema = z.object({
|
||||||
@@ -144,12 +146,12 @@ export const CreatePullRequestOptionsSchema = z.object({
|
|||||||
head: z.string(),
|
head: z.string(),
|
||||||
base: z.string(),
|
base: z.string(),
|
||||||
maintainer_can_modify: z.boolean().optional(),
|
maintainer_can_modify: z.boolean().optional(),
|
||||||
draft: z.boolean().optional()
|
draft: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CreateBranchOptionsSchema = z.object({
|
export const CreateBranchOptionsSchema = z.object({
|
||||||
ref: z.string(),
|
ref: z.string(),
|
||||||
sha: z.string()
|
sha: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Response schemas for operations
|
// Response schemas for operations
|
||||||
@@ -164,21 +166,23 @@ export const GitHubCreateUpdateFileResponseSchema = z.object({
|
|||||||
committer: GitHubAuthorSchema,
|
committer: GitHubAuthorSchema,
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
tree: z.object({
|
tree: z.object({
|
||||||
sha: z.string(),
|
|
||||||
url: z.string()
|
|
||||||
}),
|
|
||||||
parents: z.array(z.object({
|
|
||||||
sha: z.string(),
|
sha: z.string(),
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
html_url: z.string()
|
}),
|
||||||
}))
|
parents: z.array(
|
||||||
})
|
z.object({
|
||||||
|
sha: z.string(),
|
||||||
|
url: z.string(),
|
||||||
|
html_url: z.string(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubSearchResponseSchema = z.object({
|
export const GitHubSearchResponseSchema = z.object({
|
||||||
total_count: z.number(),
|
total_count: z.number(),
|
||||||
incomplete_results: z.boolean(),
|
incomplete_results: z.boolean(),
|
||||||
items: z.array(GitHubRepositorySchema)
|
items: z.array(GitHubRepositorySchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fork related schemas
|
// Fork related schemas
|
||||||
@@ -188,14 +192,14 @@ export const GitHubForkParentSchema = z.object({
|
|||||||
owner: z.object({
|
owner: z.object({
|
||||||
login: z.string(),
|
login: z.string(),
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
avatar_url: z.string()
|
avatar_url: z.string(),
|
||||||
}),
|
}),
|
||||||
html_url: z.string()
|
html_url: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubForkSchema = GitHubRepositorySchema.extend({
|
export const GitHubForkSchema = GitHubRepositorySchema.extend({
|
||||||
parent: GitHubForkParentSchema,
|
parent: GitHubForkParentSchema,
|
||||||
source: GitHubForkParentSchema
|
source: GitHubForkParentSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Issue related schemas
|
// Issue related schemas
|
||||||
@@ -206,7 +210,7 @@ export const GitHubLabelSchema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
color: z.string(),
|
color: z.string(),
|
||||||
default: z.boolean(),
|
default: z.boolean(),
|
||||||
description: z.string().optional()
|
description: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubIssueAssigneeSchema = z.object({
|
export const GitHubIssueAssigneeSchema = z.object({
|
||||||
@@ -214,7 +218,7 @@ export const GitHubIssueAssigneeSchema = z.object({
|
|||||||
id: z.number(),
|
id: z.number(),
|
||||||
avatar_url: z.string(),
|
avatar_url: z.string(),
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
html_url: z.string()
|
html_url: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubMilestoneSchema = z.object({
|
export const GitHubMilestoneSchema = z.object({
|
||||||
@@ -226,7 +230,7 @@ export const GitHubMilestoneSchema = z.object({
|
|||||||
number: z.number(),
|
number: z.number(),
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
state: z.string()
|
state: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubIssueSchema = z.object({
|
export const GitHubIssueSchema = z.object({
|
||||||
@@ -251,7 +255,7 @@ export const GitHubIssueSchema = z.object({
|
|||||||
created_at: z.string(),
|
created_at: z.string(),
|
||||||
updated_at: z.string(),
|
updated_at: z.string(),
|
||||||
closed_at: z.string().nullable(),
|
closed_at: z.string().nullable(),
|
||||||
body: z.string()
|
body: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pull Request related schemas
|
// Pull Request related schemas
|
||||||
@@ -260,7 +264,7 @@ export const GitHubPullRequestHeadSchema = z.object({
|
|||||||
ref: z.string(),
|
ref: z.string(),
|
||||||
sha: z.string(),
|
sha: z.string(),
|
||||||
user: GitHubIssueAssigneeSchema,
|
user: GitHubIssueAssigneeSchema,
|
||||||
repo: GitHubRepositorySchema
|
repo: GitHubRepositorySchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitHubPullRequestSchema = z.object({
|
export const GitHubPullRequestSchema = z.object({
|
||||||
@@ -285,12 +289,12 @@ export const GitHubPullRequestSchema = z.object({
|
|||||||
assignee: GitHubIssueAssigneeSchema.nullable(),
|
assignee: GitHubIssueAssigneeSchema.nullable(),
|
||||||
assignees: z.array(GitHubIssueAssigneeSchema),
|
assignees: z.array(GitHubIssueAssigneeSchema),
|
||||||
head: GitHubPullRequestHeadSchema,
|
head: GitHubPullRequestHeadSchema,
|
||||||
base: GitHubPullRequestHeadSchema
|
base: GitHubPullRequestHeadSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
const RepoParamsSchema = z.object({
|
const RepoParamsSchema = z.object({
|
||||||
owner: z.string().describe("Repository owner (username or organization)"),
|
owner: z.string().describe("Repository owner (username or organization)"),
|
||||||
repo: z.string().describe("Repository name")
|
repo: z.string().describe("Repository name"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CreateOrUpdateFileSchema = RepoParamsSchema.extend({
|
export const CreateOrUpdateFileSchema = RepoParamsSchema.extend({
|
||||||
@@ -298,81 +302,288 @@ export const CreateOrUpdateFileSchema = RepoParamsSchema.extend({
|
|||||||
content: z.string().describe("Content of the file"),
|
content: z.string().describe("Content of the file"),
|
||||||
message: z.string().describe("Commit message"),
|
message: z.string().describe("Commit message"),
|
||||||
branch: z.string().describe("Branch to create/update the file in"),
|
branch: z.string().describe("Branch to create/update the file in"),
|
||||||
sha: z.string().optional()
|
sha: z
|
||||||
.describe("SHA of the file being replaced (required when updating existing files)")
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
"SHA of the file being replaced (required when updating existing files)"
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SearchRepositoriesSchema = z.object({
|
export const SearchRepositoriesSchema = z.object({
|
||||||
query: z.string().describe("Search query (see GitHub search syntax)"),
|
query: z.string().describe("Search query (see GitHub search syntax)"),
|
||||||
page: z.number().optional().describe("Page number for pagination (default: 1)"),
|
page: z
|
||||||
perPage: z.number().optional().describe("Number of results per page (default: 30, max: 100)")
|
.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({
|
export const CreateRepositorySchema = z.object({
|
||||||
name: z.string().describe("Repository name"),
|
name: z.string().describe("Repository name"),
|
||||||
description: z.string().optional().describe("Repository description"),
|
description: z.string().optional().describe("Repository description"),
|
||||||
private: z.boolean().optional().describe("Whether the repository should be private"),
|
private: z
|
||||||
autoInit: z.boolean().optional().describe("Initialize with README.md")
|
.boolean()
|
||||||
|
.optional()
|
||||||
|
.describe("Whether the repository should be private"),
|
||||||
|
autoInit: z.boolean().optional().describe("Initialize with README.md"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GetFileContentsSchema = RepoParamsSchema.extend({
|
export const GetFileContentsSchema = RepoParamsSchema.extend({
|
||||||
path: z.string().describe("Path to the file or directory"),
|
path: z.string().describe("Path to the file or directory"),
|
||||||
branch: z.string().optional().describe("Branch to get contents from")
|
branch: z.string().optional().describe("Branch to get contents from"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PushFilesSchema = RepoParamsSchema.extend({
|
export const PushFilesSchema = RepoParamsSchema.extend({
|
||||||
branch: z.string().describe("Branch to push to (e.g., 'main' or 'master')"),
|
branch: z.string().describe("Branch to push to (e.g., 'main' or 'master')"),
|
||||||
files: z.array(z.object({
|
files: z
|
||||||
path: z.string().describe("Path where to create the file"),
|
.array(
|
||||||
content: z.string().describe("Content of the file")
|
z.object({
|
||||||
})).describe("Array of files to push"),
|
path: z.string().describe("Path where to create the file"),
|
||||||
message: z.string().describe("Commit message")
|
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({
|
export const CreateIssueSchema = RepoParamsSchema.extend({
|
||||||
title: z.string().describe("Issue title"),
|
title: z.string().describe("Issue title"),
|
||||||
body: z.string().optional().describe("Issue body/description"),
|
body: z.string().optional().describe("Issue body/description"),
|
||||||
assignees: z.array(z.string()).optional().describe("Array of usernames to assign"),
|
assignees: z
|
||||||
|
.array(z.string())
|
||||||
|
.optional()
|
||||||
|
.describe("Array of usernames to assign"),
|
||||||
labels: z.array(z.string()).optional().describe("Array of label names"),
|
labels: z.array(z.string()).optional().describe("Array of label names"),
|
||||||
milestone: z.number().optional().describe("Milestone number to assign")
|
milestone: z.number().optional().describe("Milestone number to assign"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CreatePullRequestSchema = RepoParamsSchema.extend({
|
export const CreatePullRequestSchema = RepoParamsSchema.extend({
|
||||||
title: z.string().describe("Pull request title"),
|
title: z.string().describe("Pull request title"),
|
||||||
body: z.string().optional().describe("Pull request body/description"),
|
body: z.string().optional().describe("Pull request body/description"),
|
||||||
head: z.string().describe("The name of the branch where your changes are implemented"),
|
head: z
|
||||||
base: z.string().describe("The name of the branch you want the changes pulled into"),
|
.string()
|
||||||
draft: z.boolean().optional().describe("Whether to create the pull request as a draft"),
|
.describe("The name of the branch where your changes are implemented"),
|
||||||
maintainer_can_modify: z.boolean().optional()
|
base: z
|
||||||
.describe("Whether maintainers can modify the pull request")
|
.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({
|
export const ForkRepositorySchema = RepoParamsSchema.extend({
|
||||||
organization: z.string().optional()
|
organization: z
|
||||||
.describe("Optional: organization to fork to (defaults to your personal account)")
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
"Optional: organization to fork to (defaults to your personal account)"
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CreateBranchSchema = RepoParamsSchema.extend({
|
export const CreateBranchSchema = RepoParamsSchema.extend({
|
||||||
branch: z.string().describe("Name for the new branch"),
|
branch: z.string().describe("Name for the new branch"),
|
||||||
from_branch: z.string().optional()
|
from_branch: z
|
||||||
.describe("Optional: source branch to create from (defaults to the repository's default branch)")
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
"Optional: source branch to create from (defaults to the repository's default branch)"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search Response Schemas
|
||||||
|
export const SearchCodeItemSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
path: z.string(),
|
||||||
|
sha: z.string(),
|
||||||
|
url: z.string(),
|
||||||
|
git_url: z.string(),
|
||||||
|
html_url: z.string(),
|
||||||
|
repository: GitHubRepositorySchema,
|
||||||
|
score: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SearchCodeResponseSchema = z.object({
|
||||||
|
total_count: z.number(),
|
||||||
|
incomplete_results: z.boolean(),
|
||||||
|
items: z.array(SearchCodeItemSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SearchIssueItemSchema = 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),
|
||||||
|
comments: z.number(),
|
||||||
|
created_at: z.string(),
|
||||||
|
updated_at: z.string(),
|
||||||
|
closed_at: z.string().nullable(),
|
||||||
|
body: z.string(),
|
||||||
|
score: z.number(),
|
||||||
|
pull_request: z
|
||||||
|
.object({
|
||||||
|
url: z.string(),
|
||||||
|
html_url: z.string(),
|
||||||
|
diff_url: z.string(),
|
||||||
|
patch_url: z.string(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SearchIssuesResponseSchema = z.object({
|
||||||
|
total_count: z.number(),
|
||||||
|
incomplete_results: z.boolean(),
|
||||||
|
items: z.array(SearchIssueItemSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SearchUserItemSchema = z.object({
|
||||||
|
login: z.string(),
|
||||||
|
id: z.number(),
|
||||||
|
node_id: z.string(),
|
||||||
|
avatar_url: z.string(),
|
||||||
|
gravatar_id: z.string(),
|
||||||
|
url: z.string(),
|
||||||
|
html_url: z.string(),
|
||||||
|
followers_url: z.string(),
|
||||||
|
following_url: z.string(),
|
||||||
|
gists_url: z.string(),
|
||||||
|
starred_url: z.string(),
|
||||||
|
subscriptions_url: z.string(),
|
||||||
|
organizations_url: z.string(),
|
||||||
|
repos_url: z.string(),
|
||||||
|
events_url: z.string(),
|
||||||
|
received_events_url: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
site_admin: z.boolean(),
|
||||||
|
score: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SearchUsersResponseSchema = z.object({
|
||||||
|
total_count: z.number(),
|
||||||
|
incomplete_results: z.boolean(),
|
||||||
|
items: z.array(SearchUserItemSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search Input Schemas
|
||||||
|
export const SearchCodeSchema = z.object({
|
||||||
|
q: z.string().describe("Search query (see GitHub code search syntax)"),
|
||||||
|
sort: z
|
||||||
|
.enum(["", "indexed"])
|
||||||
|
.optional()
|
||||||
|
.describe("Sort field (only 'indexed' is supported)"),
|
||||||
|
order: z
|
||||||
|
.enum(["asc", "desc"])
|
||||||
|
.optional()
|
||||||
|
.describe("Sort order (asc or desc)"),
|
||||||
|
per_page: z
|
||||||
|
.number()
|
||||||
|
.min(1)
|
||||||
|
.max(100)
|
||||||
|
.optional()
|
||||||
|
.describe("Results per page (max 100)"),
|
||||||
|
page: z.number().min(1).optional().describe("Page number"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SearchIssuesSchema = z.object({
|
||||||
|
q: z.string().describe("Search query (see GitHub issues search syntax)"),
|
||||||
|
sort: z
|
||||||
|
.enum([
|
||||||
|
"comments",
|
||||||
|
"reactions",
|
||||||
|
"reactions-+1",
|
||||||
|
"reactions--1",
|
||||||
|
"reactions-smile",
|
||||||
|
"reactions-thinking_face",
|
||||||
|
"reactions-heart",
|
||||||
|
"reactions-tada",
|
||||||
|
"interactions",
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
])
|
||||||
|
.optional()
|
||||||
|
.describe("Sort field"),
|
||||||
|
order: z
|
||||||
|
.enum(["asc", "desc"])
|
||||||
|
.optional()
|
||||||
|
.describe("Sort order (asc or desc)"),
|
||||||
|
per_page: z
|
||||||
|
.number()
|
||||||
|
.min(1)
|
||||||
|
.max(100)
|
||||||
|
.optional()
|
||||||
|
.describe("Results per page (max 100)"),
|
||||||
|
page: z.number().min(1).optional().describe("Page number"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SearchUsersSchema = z.object({
|
||||||
|
q: z.string().describe("Search query (see GitHub users search syntax)"),
|
||||||
|
sort: z
|
||||||
|
.enum(["followers", "repositories", "joined"])
|
||||||
|
.optional()
|
||||||
|
.describe("Sort field"),
|
||||||
|
order: z
|
||||||
|
.enum(["asc", "desc"])
|
||||||
|
.optional()
|
||||||
|
.describe("Sort order (asc or desc)"),
|
||||||
|
per_page: z
|
||||||
|
.number()
|
||||||
|
.min(1)
|
||||||
|
.max(100)
|
||||||
|
.optional()
|
||||||
|
.describe("Results per page (max 100)"),
|
||||||
|
page: z.number().min(1).optional().describe("Page number"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Export types
|
// Export types
|
||||||
export type GitHubAuthor = z.infer<typeof GitHubAuthorSchema>;
|
export type GitHubAuthor = z.infer<typeof GitHubAuthorSchema>;
|
||||||
export type GitHubFork = z.infer<typeof GitHubForkSchema>;
|
export type GitHubFork = z.infer<typeof GitHubForkSchema>;
|
||||||
export type GitHubIssue = z.infer<typeof GitHubIssueSchema>;
|
export type GitHubIssue = z.infer<typeof GitHubIssueSchema>;
|
||||||
export type GitHubPullRequest = z.infer<typeof GitHubPullRequestSchema>;export type GitHubRepository = z.infer<typeof GitHubRepositorySchema>;
|
export type GitHubPullRequest = z.infer<typeof GitHubPullRequestSchema>;
|
||||||
|
export type GitHubRepository = z.infer<typeof GitHubRepositorySchema>;
|
||||||
export type GitHubFileContent = z.infer<typeof GitHubFileContentSchema>;
|
export type GitHubFileContent = z.infer<typeof GitHubFileContentSchema>;
|
||||||
export type GitHubDirectoryContent = z.infer<typeof GitHubDirectoryContentSchema>;
|
export type GitHubDirectoryContent = z.infer<
|
||||||
|
typeof GitHubDirectoryContentSchema
|
||||||
|
>;
|
||||||
export type GitHubContent = z.infer<typeof GitHubContentSchema>;
|
export type GitHubContent = z.infer<typeof GitHubContentSchema>;
|
||||||
export type FileOperation = z.infer<typeof FileOperationSchema>;
|
export type FileOperation = z.infer<typeof FileOperationSchema>;
|
||||||
export type GitHubTree = z.infer<typeof GitHubTreeSchema>;
|
export type GitHubTree = z.infer<typeof GitHubTreeSchema>;
|
||||||
export type GitHubCommit = z.infer<typeof GitHubCommitSchema>;
|
export type GitHubCommit = z.infer<typeof GitHubCommitSchema>;
|
||||||
export type GitHubReference = z.infer<typeof GitHubReferenceSchema>;
|
export type GitHubReference = z.infer<typeof GitHubReferenceSchema>;
|
||||||
export type CreateRepositoryOptions = z.infer<typeof CreateRepositoryOptionsSchema>;
|
export type CreateRepositoryOptions = z.infer<
|
||||||
|
typeof CreateRepositoryOptionsSchema
|
||||||
|
>;
|
||||||
export type CreateIssueOptions = z.infer<typeof CreateIssueOptionsSchema>;
|
export type CreateIssueOptions = z.infer<typeof CreateIssueOptionsSchema>;
|
||||||
export type CreatePullRequestOptions = z.infer<typeof CreatePullRequestOptionsSchema>;
|
export type CreatePullRequestOptions = z.infer<
|
||||||
|
typeof CreatePullRequestOptionsSchema
|
||||||
|
>;
|
||||||
export type CreateBranchOptions = z.infer<typeof CreateBranchOptionsSchema>;
|
export type CreateBranchOptions = z.infer<typeof CreateBranchOptionsSchema>;
|
||||||
export type GitHubCreateUpdateFileResponse = z.infer<typeof GitHubCreateUpdateFileResponseSchema>;
|
export type GitHubCreateUpdateFileResponse = z.infer<
|
||||||
|
typeof GitHubCreateUpdateFileResponseSchema
|
||||||
|
>;
|
||||||
export type GitHubSearchResponse = z.infer<typeof GitHubSearchResponseSchema>;
|
export type GitHubSearchResponse = z.infer<typeof GitHubSearchResponseSchema>;
|
||||||
|
export type SearchCodeItem = z.infer<typeof SearchCodeItemSchema>;
|
||||||
|
export type SearchCodeResponse = z.infer<typeof SearchCodeResponseSchema>;
|
||||||
|
export type SearchIssueItem = z.infer<typeof SearchIssueItemSchema>;
|
||||||
|
export type SearchIssuesResponse = z.infer<typeof SearchIssuesResponseSchema>;
|
||||||
|
export type SearchUserItem = z.infer<typeof SearchUserItemSchema>;
|
||||||
|
export type SearchUsersResponse = z.infer<typeof SearchUsersResponseSchema>;
|
||||||
|
|||||||
Reference in New Issue
Block a user