Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

total glow-up: github MCP server #434

Merged
merged 39 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cecd241
fix github PR schemas
txbm Dec 28, 2024
59b831f
fix github getfilecontent zod schema to match readme spec
txbm Dec 28, 2024
90265c2
schema tweaks
txbm Dec 28, 2024
a56242d
more schema fixes
txbm Dec 28, 2024
d9ae091
more schema fixes
txbm Dec 28, 2024
f4122ff
more fixes
txbm Dec 28, 2024
0ecd204
more 'fixes'
txbm Dec 28, 2024
534b90c
Add common type definitions
txbm Dec 28, 2024
ca2c6f9
Add common utilities for GitHub API requests
txbm Dec 28, 2024
150e9cc
Add repository operations module
txbm Dec 28, 2024
ee874d7
Add file operations module
txbm Dec 28, 2024
2218a0f
Add issues operations module
txbm Dec 28, 2024
d751289
Add branches operations module
txbm Dec 28, 2024
6fdfeeb
Add pull request operations module
txbm Dec 28, 2024
7a89bd5
Add search operations module
txbm Dec 28, 2024
f8915fe
Add commits operations module
txbm Dec 28, 2024
83909dd
Refactor index.ts to use modular operation files
txbm Dec 28, 2024
b4e5754
Remove schemas.ts as schemas are now in operation modules
txbm Dec 28, 2024
9f43900
Add GitHubCreateUpdateFileResponseSchema to files module
txbm Dec 28, 2024
6b9e983
Add GitHubPullRequestSchema and related schemas to pulls module
txbm Dec 28, 2024
4ec840c
Add GitHubIssueAssigneeSchema to common types
txbm Dec 28, 2024
0b3359f
Add missing issue-related schemas to common types
txbm Dec 28, 2024
a79ec67
Add missing GitHubListCommitsSchema and GitHubSearchResponseSchema to…
txbm Dec 28, 2024
7c72d98
cleanup
txbm Dec 28, 2024
835be7f
refactor: improve pull request schemas and validation
txbm Dec 28, 2024
42872be
refactor: remove documentation and comments
txbm Dec 28, 2024
b8b7c1b
refactor: update pull request handler to use new parameter style
txbm Dec 28, 2024
e921c27
fix: restore proper runServer function closure
txbm Dec 28, 2024
fb421b4
feat: add GitHub API error handling utilities
txbm Dec 28, 2024
ff2f2c5
feat: enhance GitHub request utilities with error handling
txbm Dec 28, 2024
10bd24d
feat: enhance pull request operations with validation and error handling
txbm Dec 28, 2024
272e269
feat: add GitHub error handling to MCP server
txbm Dec 28, 2024
3e1b3ca
fix: resolve typescript errors and add buildUrl utility
txbm Dec 28, 2024
10f0aec
fix: use buildUrl utility in commits module
txbm Dec 28, 2024
dac0b7c
fix: use buildUrl utility in issues module
txbm Dec 28, 2024
8016e36
fix: use buildUrl utility in search module
txbm Dec 28, 2024
cfd6136
fix: handle URL parameter types correctly in listIssues function
txbm Dec 28, 2024
339a7b6
fix: restore original environment variable name for GitHub token
txbm Dec 28, 2024
eea524a
fix: make checkForExistingPullRequest check exact head/base match
txbm Dec 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions src/github/common/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
export class GitHubError extends Error {
constructor(
message: string,
public readonly status: number,
public readonly response: unknown
) {
super(message);
this.name = "GitHubError";
}
}

export class GitHubValidationError extends GitHubError {
constructor(message: string, status: number, response: unknown) {
super(message, status, response);
this.name = "GitHubValidationError";
}
}

export class GitHubResourceNotFoundError extends GitHubError {
constructor(resource: string) {
super(`Resource not found: ${resource}`, 404, { message: `${resource} not found` });
this.name = "GitHubResourceNotFoundError";
}
}

export class GitHubAuthenticationError extends GitHubError {
constructor(message = "Authentication failed") {
super(message, 401, { message });
this.name = "GitHubAuthenticationError";
}
}

export class GitHubPermissionError extends GitHubError {
constructor(message = "Insufficient permissions") {
super(message, 403, { message });
this.name = "GitHubPermissionError";
}
}

export class GitHubRateLimitError extends GitHubError {
constructor(
message = "Rate limit exceeded",
public readonly resetAt: Date
) {
super(message, 429, { message, reset_at: resetAt.toISOString() });
this.name = "GitHubRateLimitError";
}
}

export class GitHubConflictError extends GitHubError {
constructor(message: string) {
super(message, 409, { message });
this.name = "GitHubConflictError";
}
}

export function isGitHubError(error: unknown): error is GitHubError {
return error instanceof GitHubError;
}

export function createGitHubError(status: number, response: any): GitHubError {
switch (status) {
case 401:
return new GitHubAuthenticationError(response?.message);
case 403:
return new GitHubPermissionError(response?.message);
case 404:
return new GitHubResourceNotFoundError(response?.message || "Resource");
case 409:
return new GitHubConflictError(response?.message || "Conflict occurred");
case 422:
return new GitHubValidationError(
response?.message || "Validation failed",
status,
response
);
case 429:
return new GitHubRateLimitError(
response?.message,
new Date(response?.reset_at || Date.now() + 60000)
);
default:
return new GitHubError(
response?.message || "GitHub API error",
status,
response
);
}
}
221 changes: 221 additions & 0 deletions src/github/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import { z } from "zod";

// Base schemas for common types
export const GitHubAuthorSchema = z.object({
name: z.string(),
email: z.string(),
date: z.string(),
});

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(),
});

export const GithubFileContentLinks = z.object({
self: z.string(),
git: z.string().nullable(),
html: z.string().nullable()
});

export const GitHubFileContentSchema = z.object({
name: z.string(),
path: z.string(),
sha: z.string(),
size: z.number(),
url: z.string(),
html_url: z.string(),
git_url: z.string(),
download_url: z.string(),
type: z.string(),
content: z.string().optional(),
encoding: z.string().optional(),
_links: GithubFileContentLinks
});

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),
]);

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(),
})
),
});

export const GitHubListCommitsSchema = z.array(z.object({
sha: z.string(),
node_id: z.string(),
commit: z.object({
author: GitHubAuthorSchema,
committer: GitHubAuthorSchema,
message: z.string(),
tree: z.object({
sha: z.string(),
url: z.string()
}),
url: z.string(),
comment_count: z.number(),
}),
url: z.string(),
html_url: z.string(),
comments_url: z.string()
}));

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(),
}),
});

// User and assignee schemas
export const GitHubIssueAssigneeSchema = z.object({
login: z.string(),
id: z.number(),
avatar_url: z.string(),
url: z.string(),
html_url: z.string(),
});

// 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 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().nullable(),
});

// Search-related schemas
export const GitHubSearchResponseSchema = z.object({
total_count: z.number(),
incomplete_results: z.boolean(),
items: z.array(GitHubRepositorySchema),
});

// Export types
export type GitHubAuthor = z.infer<typeof GitHubAuthorSchema>;
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 GitHubTree = z.infer<typeof GitHubTreeSchema>;
export type GitHubCommit = z.infer<typeof GitHubCommitSchema>;
export type GitHubListCommits = z.infer<typeof GitHubListCommitsSchema>;
export type GitHubReference = z.infer<typeof GitHubReferenceSchema>;
export type GitHubIssueAssignee = z.infer<typeof GitHubIssueAssigneeSchema>;
export type GitHubLabel = z.infer<typeof GitHubLabelSchema>;
export type GitHubMilestone = z.infer<typeof GitHubMilestoneSchema>;
export type GitHubIssue = z.infer<typeof GitHubIssueSchema>;
export type GitHubSearchResponse = z.infer<typeof GitHubSearchResponseSchema>;
Loading