From eb3a8980dd3a9306da19082827ea4cc932476576 Mon Sep 17 00:00:00 2001 From: ewired <37567272+ewired@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:30:35 -0500 Subject: [PATCH 1/4] feat: add GitHub releases listing support Adds new MCP tool to list releases for a GitHub repository with: - Release and asset schemas in types.ts - List releases operation implementation - Tool registration in index.ts --- src/github/common/types.ts | 39 +++++++++++++++++++++++++++++++ src/github/index.ts | 14 +++++++++++ src/github/operations/releases.ts | 29 +++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 src/github/operations/releases.ts diff --git a/src/github/common/types.ts b/src/github/common/types.ts index cca961ba..b2bed418 100644 --- a/src/github/common/types.ts +++ b/src/github/common/types.ts @@ -256,4 +256,43 @@ export type GitHubMilestone = z.infer; export type GitHubIssue = z.infer; export type GitHubSearchResponse = z.infer; export type GitHubPullRequest = z.infer; +// Release schemas +export const GitHubReleaseAssetSchema = z.object({ + url: z.string(), + id: z.number(), + node_id: z.string(), + name: z.string(), + label: z.string().nullable(), + content_type: z.string(), + state: z.string(), + size: z.number(), + download_count: z.number(), + created_at: z.string(), + updated_at: z.string(), + browser_download_url: z.string() +}); + +export const GitHubReleaseSchema = z.object({ + url: z.string(), + assets_url: z.string(), + upload_url: z.string(), + html_url: z.string(), + id: z.number(), + node_id: z.string(), + tag_name: z.string(), + target_commitish: z.string(), + name: z.string().nullable(), + draft: z.boolean(), + prerelease: z.boolean(), + created_at: z.string(), + published_at: z.string().nullable(), + assets: z.array(GitHubReleaseAssetSchema), + tarball_url: z.string(), + zipball_url: z.string(), + body: z.string().nullable(), + author: GitHubOwnerSchema +}); + +export type GitHubRelease = z.infer; +export type GitHubReleaseAsset = z.infer; export type GitHubPullRequestRef = z.infer; \ No newline at end of file diff --git a/src/github/index.ts b/src/github/index.ts index 3d60e8fa..4d8fe3db 100644 --- a/src/github/index.ts +++ b/src/github/index.ts @@ -15,6 +15,7 @@ import * as pulls from './operations/pulls.js'; import * as branches from './operations/branches.js'; import * as search from './operations/search.js'; import * as commits from './operations/commits.js'; +import * as releases from './operations/releases.js'; import { GitHubError, GitHubValidationError, @@ -148,6 +149,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { name: "get_issue", description: "Get details of a specific issue in a GitHub repository.", inputSchema: zodToJsonSchema(issues.GetIssueSchema) + }, + { + name: "list_releases", + description: "List releases for a GitHub repository", + inputSchema: zodToJsonSchema(releases.ListReleasesOptionsSchema) } ], }; @@ -334,6 +340,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }; } + case "list_releases": { + const args = releases.ListReleasesOptionsSchema.parse(request.params.arguments); + const releasesList = await releases.listReleases(args); + return { + content: [{ type: "text", text: JSON.stringify(releasesList, null, 2) }], + }; + } + default: throw new Error(`Unknown tool: ${request.params.name}`); } diff --git a/src/github/operations/releases.ts b/src/github/operations/releases.ts new file mode 100644 index 00000000..d9f0482b --- /dev/null +++ b/src/github/operations/releases.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; +import { buildUrl, githubRequest } from "../common/utils.js"; +import { GitHubReleaseSchema } from "../common/types.js"; + +// Schema definitions +export const ListReleasesOptionsSchema = z.object({ + owner: z.string().describe("Repository owner (username or organization)"), + repo: z.string().describe("Repository name"), + 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 type ListReleasesOptions = z.infer; + +// Function implementations +export async function listReleases({ + owner, + repo, + page = 1, + perPage = 30, +}: ListReleasesOptions) { + const url = buildUrl(`https://api.github.com/repos/${owner}/${repo}/releases`, { + page: page.toString(), + per_page: perPage.toString(), + }); + + const response = await githubRequest(url); + return z.array(GitHubReleaseSchema).parse(response); +} \ No newline at end of file From a3fe09acefef26fc3d225f83b97df440dbfe9fc3 Mon Sep 17 00:00:00 2001 From: ewired <37567272+ewired@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:38:37 -0500 Subject: [PATCH 2/4] feat: add GitHub latest release tool Adds new MCP tool to get only the latest release for a repository: - GetLatestRelease schema and type - Latest release operation implementation - Tool registration in index.ts This helps reduce context sizes by providing a focused way to get just the latest release data. --- src/github/index.ts | 13 +++++++++++++ src/github/operations/releases.ts | 15 +++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/github/index.ts b/src/github/index.ts index 4d8fe3db..d2beb480 100644 --- a/src/github/index.ts +++ b/src/github/index.ts @@ -154,6 +154,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { name: "list_releases", description: "List releases for a GitHub repository", inputSchema: zodToJsonSchema(releases.ListReleasesOptionsSchema) + }, + { + name: "get_latest_release", + description: "Get the latest release for a GitHub repository", + inputSchema: zodToJsonSchema(releases.GetLatestReleaseSchema) } ], }; @@ -348,6 +353,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }; } + case "get_latest_release": { + const args = releases.GetLatestReleaseSchema.parse(request.params.arguments); + const latestRelease = await releases.getLatestRelease(args); + return { + content: [{ type: "text", text: JSON.stringify(latestRelease, null, 2) }], + }; + } + default: throw new Error(`Unknown tool: ${request.params.name}`); } diff --git a/src/github/operations/releases.ts b/src/github/operations/releases.ts index d9f0482b..35cad36b 100644 --- a/src/github/operations/releases.ts +++ b/src/github/operations/releases.ts @@ -10,7 +10,13 @@ export const ListReleasesOptionsSchema = z.object({ perPage: z.number().optional().describe("Number of results per page (default: 30, max: 100)"), }); +export const GetLatestReleaseSchema = z.object({ + owner: z.string().describe("Repository owner (username or organization)"), + repo: z.string().describe("Repository name"), +}); + export type ListReleasesOptions = z.infer; +export type GetLatestReleaseOptions = z.infer; // Function implementations export async function listReleases({ @@ -26,4 +32,13 @@ export async function listReleases({ const response = await githubRequest(url); return z.array(GitHubReleaseSchema).parse(response); +} + +export async function getLatestRelease({ + owner, + repo, +}: GetLatestReleaseOptions) { + const url = `https://api.github.com/repos/${owner}/${repo}/releases/latest`; + const response = await githubRequest(url); + return GitHubReleaseSchema.parse(response); } \ No newline at end of file From fbcaa3440af9a8c522a7f35021cff62983db4801 Mon Sep 17 00:00:00 2001 From: ewired <37567272+ewired@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:43:52 -0500 Subject: [PATCH 3/4] feat: add includeAssets option to release tools Adds optional includeAssets parameter (default: false) to both release tools: - Modified GitHubReleaseSchema to make assets optional - Added includeAssets option to ListReleasesOptions and GetLatestReleaseOptions - Updated functions to strip assets when not requested This helps further reduce response sizes when asset data isn't needed. --- src/github/common/types.ts | 8 +++++++- src/github/operations/releases.ts | 24 ++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/github/common/types.ts b/src/github/common/types.ts index b2bed418..18fde809 100644 --- a/src/github/common/types.ts +++ b/src/github/common/types.ts @@ -286,11 +286,17 @@ export const GitHubReleaseSchema = z.object({ prerelease: z.boolean(), created_at: z.string(), published_at: z.string().nullable(), - assets: z.array(GitHubReleaseAssetSchema), + assets: z.array(GitHubReleaseAssetSchema).optional(), tarball_url: z.string(), zipball_url: z.string(), body: z.string().nullable(), author: GitHubOwnerSchema +}).transform(data => { + // Ensure assets is always an array even if not included + return { + ...data, + assets: data.assets || [] + }; }); export type GitHubRelease = z.infer; diff --git a/src/github/operations/releases.ts b/src/github/operations/releases.ts index 35cad36b..b74f2b51 100644 --- a/src/github/operations/releases.ts +++ b/src/github/operations/releases.ts @@ -8,11 +8,13 @@ export const ListReleasesOptionsSchema = z.object({ repo: z.string().describe("Repository name"), 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)"), + includeAssets: z.boolean().optional().describe("Whether to include release assets in the response (default: false)") }); export const GetLatestReleaseSchema = z.object({ owner: z.string().describe("Repository owner (username or organization)"), repo: z.string().describe("Repository name"), + includeAssets: z.boolean().optional().describe("Whether to include release assets in the response (default: false)") }); export type ListReleasesOptions = z.infer; @@ -24,6 +26,7 @@ export async function listReleases({ repo, page = 1, perPage = 30, + includeAssets = false }: ListReleasesOptions) { const url = buildUrl(`https://api.github.com/repos/${owner}/${repo}/releases`, { page: page.toString(), @@ -31,14 +34,31 @@ export async function listReleases({ }); const response = await githubRequest(url); - return z.array(GitHubReleaseSchema).parse(response); + const releases = z.array(GitHubReleaseSchema).parse(response); + + if (!includeAssets) { + return releases.map(release => { + const { assets, ...rest } = release; + return { ...rest, assets: [] }; + }); + } + + return releases; } export async function getLatestRelease({ owner, repo, + includeAssets = false }: GetLatestReleaseOptions) { const url = `https://api.github.com/repos/${owner}/${repo}/releases/latest`; const response = await githubRequest(url); - return GitHubReleaseSchema.parse(response); + const release = GitHubReleaseSchema.parse(response); + + if (!includeAssets) { + const { assets, ...rest } = release; + return { ...rest, assets: [] }; + } + + return release; } \ No newline at end of file From c0a39adb09a682001a908b289af3b1304f18fe21 Mon Sep 17 00:00:00 2001 From: ewired <37567272+ewired@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:51:39 -0500 Subject: [PATCH 4/4] docs: add release tools documentation Adds documentation for new GitHub release tools: - Updated Features section to highlight release management - Added list_releases and get_latest_release tool documentation - Included details about optional asset inclusion --- src/github/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/github/README.md b/src/github/README.md index 026dde9b..88107357 100644 --- a/src/github/README.md +++ b/src/github/README.md @@ -9,6 +9,7 @@ MCP Server for the GitHub API, enabling file operations, repository management, - **Git History Preservation**: Operations maintain proper Git history without force pushing - **Batch Operations**: Support for both single-file and multi-file operations - **Advanced Search**: Support for searching code, issues/PRs, and users +- **Get Repo Releases**: Retrieve the latest releases of a repository to assist with dependency management ## Tools @@ -277,6 +278,24 @@ MCP Server for the GitHub API, enabling file operations, repository management, - `pull_number` (number): Pull request number - Returns: Array of pull request reviews with details like the review state (APPROVED, CHANGES_REQUESTED, etc.), reviewer, and review body +27. `list_releases` + - List releases for a GitHub repository + - Inputs: + - `owner` (string): Repository owner + - `repo` (string): Repository name + - `page` (optional number): Page number for pagination + - `perPage` (optional number): Results per page (max 100) + - `includeAssets` (optional boolean): Whether to include release assets (default: false) + - Returns: Array of repository releases with metadata (and optionally assets) + +28. `get_latest_release` + - Get the latest release for a GitHub repository + - Inputs: + - `owner` (string): Repository owner + - `repo` (string): Repository name + - `includeAssets` (optional boolean): Whether to include release assets (default: false) + - Returns: Latest release with metadata (and optionally assets) + ## Search Query Syntax ### Code Search