Skip to content

Commit a2967d1

Browse files
committed
total glow-up: github MCP server (PR #434)
2 parents 4f3dc11 + 80b52d9 commit a2967d1

File tree

12 files changed

+1403
-1522
lines changed

12 files changed

+1403
-1522
lines changed

src/github/common/errors.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
export class GitHubError extends Error {
2+
constructor(
3+
message: string,
4+
public readonly status: number,
5+
public readonly response: unknown
6+
) {
7+
super(message);
8+
this.name = "GitHubError";
9+
}
10+
}
11+
12+
export class GitHubValidationError extends GitHubError {
13+
constructor(message: string, status: number, response: unknown) {
14+
super(message, status, response);
15+
this.name = "GitHubValidationError";
16+
}
17+
}
18+
19+
export class GitHubResourceNotFoundError extends GitHubError {
20+
constructor(resource: string) {
21+
super(`Resource not found: ${resource}`, 404, { message: `${resource} not found` });
22+
this.name = "GitHubResourceNotFoundError";
23+
}
24+
}
25+
26+
export class GitHubAuthenticationError extends GitHubError {
27+
constructor(message = "Authentication failed") {
28+
super(message, 401, { message });
29+
this.name = "GitHubAuthenticationError";
30+
}
31+
}
32+
33+
export class GitHubPermissionError extends GitHubError {
34+
constructor(message = "Insufficient permissions") {
35+
super(message, 403, { message });
36+
this.name = "GitHubPermissionError";
37+
}
38+
}
39+
40+
export class GitHubRateLimitError extends GitHubError {
41+
constructor(
42+
message = "Rate limit exceeded",
43+
public readonly resetAt: Date
44+
) {
45+
super(message, 429, { message, reset_at: resetAt.toISOString() });
46+
this.name = "GitHubRateLimitError";
47+
}
48+
}
49+
50+
export class GitHubConflictError extends GitHubError {
51+
constructor(message: string) {
52+
super(message, 409, { message });
53+
this.name = "GitHubConflictError";
54+
}
55+
}
56+
57+
export function isGitHubError(error: unknown): error is GitHubError {
58+
return error instanceof GitHubError;
59+
}
60+
61+
export function createGitHubError(status: number, response: any): GitHubError {
62+
switch (status) {
63+
case 401:
64+
return new GitHubAuthenticationError(response?.message);
65+
case 403:
66+
return new GitHubPermissionError(response?.message);
67+
case 404:
68+
return new GitHubResourceNotFoundError(response?.message || "Resource");
69+
case 409:
70+
return new GitHubConflictError(response?.message || "Conflict occurred");
71+
case 422:
72+
return new GitHubValidationError(
73+
response?.message || "Validation failed",
74+
status,
75+
response
76+
);
77+
case 429:
78+
return new GitHubRateLimitError(
79+
response?.message,
80+
new Date(response?.reset_at || Date.now() + 60000)
81+
);
82+
default:
83+
return new GitHubError(
84+
response?.message || "GitHub API error",
85+
status,
86+
response
87+
);
88+
}
89+
}

src/github/common/types.ts

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import { z } from "zod";
2+
3+
// Base schemas for common types
4+
export const GitHubAuthorSchema = z.object({
5+
name: z.string(),
6+
email: z.string(),
7+
date: z.string(),
8+
});
9+
10+
export const GitHubOwnerSchema = z.object({
11+
login: z.string(),
12+
id: z.number(),
13+
node_id: z.string(),
14+
avatar_url: z.string(),
15+
url: z.string(),
16+
html_url: z.string(),
17+
type: z.string(),
18+
});
19+
20+
export const GitHubRepositorySchema = z.object({
21+
id: z.number(),
22+
node_id: z.string(),
23+
name: z.string(),
24+
full_name: z.string(),
25+
private: z.boolean(),
26+
owner: GitHubOwnerSchema,
27+
html_url: z.string(),
28+
description: z.string().nullable(),
29+
fork: z.boolean(),
30+
url: z.string(),
31+
created_at: z.string(),
32+
updated_at: z.string(),
33+
pushed_at: z.string(),
34+
git_url: z.string(),
35+
ssh_url: z.string(),
36+
clone_url: z.string(),
37+
default_branch: z.string(),
38+
});
39+
40+
export const GithubFileContentLinks = z.object({
41+
self: z.string(),
42+
git: z.string().nullable(),
43+
html: z.string().nullable()
44+
});
45+
46+
export const GitHubFileContentSchema = z.object({
47+
name: z.string(),
48+
path: z.string(),
49+
sha: z.string(),
50+
size: z.number(),
51+
url: z.string(),
52+
html_url: z.string(),
53+
git_url: z.string(),
54+
download_url: z.string(),
55+
type: z.string(),
56+
content: z.string().optional(),
57+
encoding: z.string().optional(),
58+
_links: GithubFileContentLinks
59+
});
60+
61+
export const GitHubDirectoryContentSchema = z.object({
62+
type: z.string(),
63+
size: z.number(),
64+
name: z.string(),
65+
path: z.string(),
66+
sha: z.string(),
67+
url: z.string(),
68+
git_url: z.string(),
69+
html_url: z.string(),
70+
download_url: z.string().nullable(),
71+
});
72+
73+
export const GitHubContentSchema = z.union([
74+
GitHubFileContentSchema,
75+
z.array(GitHubDirectoryContentSchema),
76+
]);
77+
78+
export const GitHubTreeEntrySchema = z.object({
79+
path: z.string(),
80+
mode: z.enum(["100644", "100755", "040000", "160000", "120000"]),
81+
type: z.enum(["blob", "tree", "commit"]),
82+
size: z.number().optional(),
83+
sha: z.string(),
84+
url: z.string(),
85+
});
86+
87+
export const GitHubTreeSchema = z.object({
88+
sha: z.string(),
89+
url: z.string(),
90+
tree: z.array(GitHubTreeEntrySchema),
91+
truncated: z.boolean(),
92+
});
93+
94+
export const GitHubCommitSchema = z.object({
95+
sha: z.string(),
96+
node_id: z.string(),
97+
url: z.string(),
98+
author: GitHubAuthorSchema,
99+
committer: GitHubAuthorSchema,
100+
message: z.string(),
101+
tree: z.object({
102+
sha: z.string(),
103+
url: z.string(),
104+
}),
105+
parents: z.array(
106+
z.object({
107+
sha: z.string(),
108+
url: z.string(),
109+
})
110+
),
111+
});
112+
113+
export const GitHubListCommitsSchema = z.array(z.object({
114+
sha: z.string(),
115+
node_id: z.string(),
116+
commit: z.object({
117+
author: GitHubAuthorSchema,
118+
committer: GitHubAuthorSchema,
119+
message: z.string(),
120+
tree: z.object({
121+
sha: z.string(),
122+
url: z.string()
123+
}),
124+
url: z.string(),
125+
comment_count: z.number(),
126+
}),
127+
url: z.string(),
128+
html_url: z.string(),
129+
comments_url: z.string()
130+
}));
131+
132+
export const GitHubReferenceSchema = z.object({
133+
ref: z.string(),
134+
node_id: z.string(),
135+
url: z.string(),
136+
object: z.object({
137+
sha: z.string(),
138+
type: z.string(),
139+
url: z.string(),
140+
}),
141+
});
142+
143+
// User and assignee schemas
144+
export const GitHubIssueAssigneeSchema = z.object({
145+
login: z.string(),
146+
id: z.number(),
147+
avatar_url: z.string(),
148+
url: z.string(),
149+
html_url: z.string(),
150+
});
151+
152+
// Issue-related schemas
153+
export const GitHubLabelSchema = z.object({
154+
id: z.number(),
155+
node_id: z.string(),
156+
url: z.string(),
157+
name: z.string(),
158+
color: z.string(),
159+
default: z.boolean(),
160+
description: z.string().optional(),
161+
});
162+
163+
export const GitHubMilestoneSchema = z.object({
164+
url: z.string(),
165+
html_url: z.string(),
166+
labels_url: z.string(),
167+
id: z.number(),
168+
node_id: z.string(),
169+
number: z.number(),
170+
title: z.string(),
171+
description: z.string(),
172+
state: z.string(),
173+
});
174+
175+
export const GitHubIssueSchema = z.object({
176+
url: z.string(),
177+
repository_url: z.string(),
178+
labels_url: z.string(),
179+
comments_url: z.string(),
180+
events_url: z.string(),
181+
html_url: z.string(),
182+
id: z.number(),
183+
node_id: z.string(),
184+
number: z.number(),
185+
title: z.string(),
186+
user: GitHubIssueAssigneeSchema,
187+
labels: z.array(GitHubLabelSchema),
188+
state: z.string(),
189+
locked: z.boolean(),
190+
assignee: GitHubIssueAssigneeSchema.nullable(),
191+
assignees: z.array(GitHubIssueAssigneeSchema),
192+
milestone: GitHubMilestoneSchema.nullable(),
193+
comments: z.number(),
194+
created_at: z.string(),
195+
updated_at: z.string(),
196+
closed_at: z.string().nullable(),
197+
body: z.string().nullable(),
198+
});
199+
200+
// Search-related schemas
201+
export const GitHubSearchResponseSchema = z.object({
202+
total_count: z.number(),
203+
incomplete_results: z.boolean(),
204+
items: z.array(GitHubRepositorySchema),
205+
});
206+
207+
// Export types
208+
export type GitHubAuthor = z.infer<typeof GitHubAuthorSchema>;
209+
export type GitHubRepository = z.infer<typeof GitHubRepositorySchema>;
210+
export type GitHubFileContent = z.infer<typeof GitHubFileContentSchema>;
211+
export type GitHubDirectoryContent = z.infer<typeof GitHubDirectoryContentSchema>;
212+
export type GitHubContent = z.infer<typeof GitHubContentSchema>;
213+
export type GitHubTree = z.infer<typeof GitHubTreeSchema>;
214+
export type GitHubCommit = z.infer<typeof GitHubCommitSchema>;
215+
export type GitHubListCommits = z.infer<typeof GitHubListCommitsSchema>;
216+
export type GitHubReference = z.infer<typeof GitHubReferenceSchema>;
217+
export type GitHubIssueAssignee = z.infer<typeof GitHubIssueAssigneeSchema>;
218+
export type GitHubLabel = z.infer<typeof GitHubLabelSchema>;
219+
export type GitHubMilestone = z.infer<typeof GitHubMilestoneSchema>;
220+
export type GitHubIssue = z.infer<typeof GitHubIssueSchema>;
221+
export type GitHubSearchResponse = z.infer<typeof GitHubSearchResponseSchema>;

0 commit comments

Comments
 (0)