github-cicd-mcp 0.0.0-stage → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,43 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.2.0] - 2026-10-13
6
+
7
+ ### Added
8
+
9
+ - SECURITY.md for responsible vulnerability disclosure
10
+ - Security policy and best practices documentation
11
+ - CI/CD verification script that counts registered tools (70 tools)
12
+ - Enhanced documentation for public release
13
+
14
+ ### Changed
15
+
16
+ - Updated package.json with correct repository URLs
17
+ - Improved README with installation instructions for npm and source
18
+ - Added npm publish configuration for public access
19
+
20
+ ### Security
21
+
22
+ - Added security checklist and token scope recommendations
23
+ - Documented safe authentication practices
24
+ - Added dependency audit information
25
+
26
+ ## [1.1.0] - 2026-03-23
27
+
28
+ ### Added
29
+
30
+ - Issues: create, list, get, update, comments
31
+ - Releases and tags
32
+ - Branch protection and branch listing
33
+ - Webhooks and collaborators / org teams
34
+ - GraphQL query, mutation, and repository overview helper
35
+ - Workflow run artifacts
36
+ - Fork repository
37
+ - Public release docs (`RELEASING.md`), CI workflow, MIT `LICENSE`
38
+
39
+ ## [1.0.0] - 2026-03-23
40
+
41
+ ### Added
42
+
43
+ - Initial MCP: repos, commits, PRs, Actions, deployments, billing, local git
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 github-cicd-mcp contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,146 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # github-cicd-mcp
2
+
3
+ GitHub CI/CD MCP server for Model Context Protocol.
4
+
5
+ [![CI](https://github.com/devstephenke-sys/Github-Free-MCPs/actions/workflows/ci.yml/badge.svg)](https://github.com/devstephenke-sys/Github-Free-MCPs/actions/workflows/ci.yml)
6
+ [![npm version](https://img.shields.io/npm/v/github-cicd-mcp.svg)](https://www.npmjs.com/package/github-cicd-mcp)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
+
9
+ **Model Context Protocol (MCP) server** for [GitHub REST](https://docs.github.com/en/rest) and [GraphQL](https://docs.github.com/en/graphql) — built for **CI/CD and DevOps** workflows in Cursor, Claude Desktop, and other MCP clients.
10
+
11
+ Give your AI agent tools to manage repos, commits, pull requests, issues, GitHub Actions, deployments, billing, releases, branch protection, webhooks, and local `git` — without leaving the editor.
12
+
13
+ ---
14
+
15
+ ## Install
16
+
17
+ ### npm / npx (recommended)
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "github-cicd": {
23
+ "command": "npx",
24
+ "args": ["-y", "github-cicd-mcp"],
25
+ "env": {
26
+ "GITHUB_TOKEN": "${env:GITHUB_TOKEN}"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ Set `GITHUB_TOKEN` (or `GH_TOKEN`) in your system environment, or replace `${env:GITHUB_TOKEN}` with a secret reference your client supports.
34
+
35
+ ### From source
36
+
37
+ ```bash
38
+ git clone https://github.com/devstephenke-sys/Github-Free-MCPs.git
39
+ cd Github-Free-MCPs
40
+ npm install
41
+ npm run build
42
+ ```
43
+
44
+ Point MCP at `node /absolute/path/to/github-cicd-mcp/dist/index.js`.
45
+
46
+ ---
47
+
48
+ ## Authentication
49
+
50
+ Create a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens):
51
+
52
+ | Need | Scopes / permissions |
53
+ |------|----------------------|
54
+ | Repos, PRs, commits, issues | `repo` or fine-grained **Contents**, **Pull requests**, **Issues** |
55
+ | Actions | `workflow` |
56
+ | Org billing | `read:org` + org billing role |
57
+ | Admin (branch protection, webhooks) | Admin access on the repo/org |
58
+
59
+ Never commit tokens. Use environment variables only.
60
+
61
+ ⚠️ **Security**: See [SECURITY.md](SECURITY.md) for security policies and best practices.
62
+
63
+ ---
64
+
65
+ ## Tool reference
66
+
67
+ ### Core & repos
68
+
69
+ `github_get_authenticated_user`, `github_get_rate_limit`, `github_create_repository`, `github_get_repository`, `github_list_repositories`, `github_create_branch`, `github_fork_repository`
70
+
71
+ ### Commits & CI status
72
+
73
+ `github_list_commits`, `github_get_commit`, `github_compare_commits`, `github_create_commit`, `github_get_combined_status`, `github_list_check_runs_for_ref`
74
+
75
+ ### Pull requests
76
+
77
+ `github_create_pull_request`, `github_list_pull_requests`, `github_get_pull_request`, `github_merge_pull_request`, `github_update_pull_request`, `github_list_pull_request_files`, `github_create_pull_request_review`
78
+
79
+ ### Issues
80
+
81
+ `github_create_issue`, `github_list_issues`, `github_get_issue`, `github_update_issue`, `github_add_issue_comment`, `github_list_issue_comments`
82
+
83
+ ### Releases & tags
84
+
85
+ `github_list_releases`, `github_get_release_by_tag`, `github_create_release`, `github_list_tags`, `github_create_tag`
86
+
87
+ ### Branches & protection
88
+
89
+ `github_list_branches`, `github_get_branch`, `github_get_branch_protection`, `github_update_branch_protection`, `github_delete_branch_protection`
90
+
91
+ ### GitHub Actions
92
+
93
+ `github_list_workflows`, `github_list_workflow_runs`, `github_get_workflow_run`, `github_list_workflow_jobs`, `github_rerun_workflow`, `github_cancel_workflow_run`, `github_dispatch_workflow`, `github_list_actions_secrets`, `github_list_actions_variables`, `github_list_workflow_run_artifacts`, `github_download_workflow_artifact`
94
+
95
+ ### Deployments (CD)
96
+
97
+ `github_list_deployments`, `github_create_deployment`, `github_create_deployment_status`, `github_list_environments`
98
+
99
+ ### Billing
100
+
101
+ `github_get_user_billing_actions`, `github_get_org_billing_actions`, `github_get_org_billing_overview`, `github_get_org_billing_usage_report`
102
+
103
+ ### Webhooks & access
104
+
105
+ `github_list_webhooks`, `github_create_webhook`, `github_delete_webhook`, `github_list_collaborators`, `github_add_collaborator`, `github_list_teams`
106
+
107
+ ### GraphQL
108
+
109
+ `github_graphql_query`, `github_graphql_mutation`, `github_graphql_repository_overview`
110
+
111
+ ### Local git
112
+
113
+ `git_status`, `git_commit`, `git_push`, `git_pull`, `git_log`, `git_clone`
114
+
115
+ ---
116
+
117
+ ## GitHub Enterprise
118
+
119
+ - `GITHUB_HOST` — hostname (when not `github.com`)
120
+ - `GITHUB_API_URL` — REST base, e.g. `https://github.example.com/api/v3`
121
+ - `GITHUB_GRAPHQL_URL` — GraphQL endpoint override
122
+
123
+ ---
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ npm install
129
+ npm run build
130
+ npm run verify
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Publish your own fork
136
+
137
+ See **[RELEASING.md](RELEASING.md)** for:
138
+ - Public GitHub repo setup
139
+ - npm publish (`npm publish --access public`)
140
+ - GitHub Releases and optional MCP directory listings
141
+
142
+ ---
143
+
144
+ ## License
145
+
146
+ [MIT](LICENSE)
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { registerActionsTools } from "./tools/actions.js";
5
+ import { registerBillingTools } from "./tools/billing.js";
6
+ import { registerBranchProtectionTools } from "./tools/branch-protection.js";
7
+ import { registerCommitTools } from "./tools/commits.js";
8
+ import { registerDeploymentTools } from "./tools/deployments.js";
9
+ import { registerGraphqlTools } from "./tools/graphql.js";
10
+ import { registerIssueTools } from "./tools/issues.js";
11
+ import { registerLocalGitTools } from "./tools/local-git.js";
12
+ import { registerPullRequestTools } from "./tools/pull-requests.js";
13
+ import { registerReleaseTools } from "./tools/releases.js";
14
+ import { registerRepoTools } from "./tools/repos.js";
15
+ import { registerCollaboratorTools, registerWebhookTools, } from "./tools/webhooks-collaborators.js";
16
+ const server = new McpServer({
17
+ name: "github-cicd-mcp",
18
+ version: "1.1.0",
19
+ }, {
20
+ instructions: `GitHub CI/CD assistant (REST + GraphQL): https://docs.github.com/en/rest and /en/graphql.
21
+ github_* — remote API (repos, commits, PRs, issues, releases, branch protection, Actions, deployments, billing, webhooks).
22
+ git_* — local clone/commit/push/pull.
23
+ Requires GITHUB_TOKEN or GH_TOKEN (repo, workflow; read:org for org billing).`,
24
+ });
25
+ registerRepoTools(server);
26
+ registerCommitTools(server);
27
+ registerPullRequestTools(server);
28
+ registerActionsTools(server);
29
+ registerDeploymentTools(server);
30
+ registerBillingTools(server);
31
+ registerIssueTools(server);
32
+ registerReleaseTools(server);
33
+ registerBranchProtectionTools(server);
34
+ registerWebhookTools(server);
35
+ registerCollaboratorTools(server);
36
+ registerGraphqlTools(server);
37
+ registerLocalGitTools(server);
38
+ async function main() {
39
+ const transport = new StdioServerTransport();
40
+ await server.connect(transport);
41
+ }
42
+ main().catch((err) => {
43
+ console.error(err);
44
+ process.exit(1);
45
+ });
@@ -0,0 +1,4 @@
1
+ import { z } from "zod";
2
+ export declare const owner: z.ZodString;
3
+ export declare const repo: z.ZodString;
4
+ export declare const perPage: z.ZodOptional<z.ZodNumber>;
@@ -0,0 +1,4 @@
1
+ import { z } from "zod";
2
+ export const owner = z.string().describe("Repository owner (user or org login)");
3
+ export const repo = z.string().describe("Repository name");
4
+ export const perPage = z.number().optional().describe("Results per page (max 100)");
@@ -0,0 +1,13 @@
1
+ import { Octokit } from "@octokit/rest";
2
+ export declare function getToken(): string;
3
+ export declare function getOctokit(): Octokit;
4
+ export declare const ownerRepoSchema: {
5
+ owner: {
6
+ type: "string";
7
+ description: string;
8
+ };
9
+ repo: {
10
+ type: "string";
11
+ description: string;
12
+ };
13
+ };
@@ -0,0 +1,27 @@
1
+ import { Octokit } from "@octokit/rest";
2
+ let octokitInstance = null;
3
+ export function getToken() {
4
+ const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN ?? "";
5
+ if (!token) {
6
+ throw new Error("Missing GITHUB_TOKEN (or GH_TOKEN). Create a PAT: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens");
7
+ }
8
+ return token;
9
+ }
10
+ export function getOctokit() {
11
+ if (!octokitInstance) {
12
+ const baseUrl = process.env.GITHUB_API_URL ??
13
+ (process.env.GITHUB_HOST && process.env.GITHUB_HOST !== "github.com"
14
+ ? `https://${process.env.GITHUB_HOST}/api/v3`
15
+ : undefined);
16
+ octokitInstance = new Octokit({
17
+ auth: getToken(),
18
+ userAgent: "github-cicd-mcp/1.1.0",
19
+ ...(baseUrl ? { baseUrl } : {}),
20
+ });
21
+ }
22
+ return octokitInstance;
23
+ }
24
+ export const ownerRepoSchema = {
25
+ owner: { type: "string", description: "Repository owner (user or org login)" },
26
+ repo: { type: "string", description: "Repository name" },
27
+ };
@@ -0,0 +1,4 @@
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ export declare function jsonResult(data: unknown): CallToolResult;
3
+ export declare function textResult(message: string): CallToolResult;
4
+ export declare function errorResult(err: unknown): CallToolResult;
@@ -0,0 +1,48 @@
1
+ import { RequestError } from "@octokit/request-error";
2
+ function formatUnknownError(err) {
3
+ if (err instanceof RequestError) {
4
+ const detail = err.response?.data !== undefined
5
+ ? typeof err.response.data === "string"
6
+ ? err.response.data
7
+ : JSON.stringify(err.response.data, null, 2)
8
+ : "";
9
+ const requestId = err.response?.headers && "x-github-request-id" in err.response.headers
10
+ ? String(err.response.headers["x-github-request-id"])
11
+ : undefined;
12
+ return [
13
+ `GitHub API ${err.status}: ${err.message}`,
14
+ requestId ? `request-id: ${requestId}` : "",
15
+ detail ? detail : "",
16
+ ]
17
+ .filter(Boolean)
18
+ .join("\n");
19
+ }
20
+ if (err instanceof Error) {
21
+ return err.message;
22
+ }
23
+ if (typeof err === "object" && err !== null && "message" in err) {
24
+ return String(err.message);
25
+ }
26
+ return String(err);
27
+ }
28
+ export function jsonResult(data) {
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: JSON.stringify(data, null, 2),
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ export function textResult(message) {
39
+ return {
40
+ content: [{ type: "text", text: message }],
41
+ };
42
+ }
43
+ export function errorResult(err) {
44
+ return {
45
+ content: [{ type: "text", text: `Error: ${formatUnknownError(err)}` }],
46
+ isError: true,
47
+ };
48
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerActionsTools(server: McpServer): void;
@@ -0,0 +1,257 @@
1
+ import { z } from "zod";
2
+ import { owner, perPage, repo } from "../lib/fields.js";
3
+ import { getOctokit } from "../lib/github.js";
4
+ import { errorResult, jsonResult } from "../lib/response.js";
5
+ const workflowStatus = z
6
+ .string()
7
+ .optional()
8
+ .describe("queued | in_progress | completed | failure | success | etc.");
9
+ export function registerActionsTools(server) {
10
+ server.tool("github_list_workflows", "List GitHub Actions workflows in a repository.", { owner, repo }, async ({ owner, repo }) => {
11
+ try {
12
+ const { data } = await getOctokit().rest.actions.listRepoWorkflows({
13
+ owner,
14
+ repo,
15
+ });
16
+ return jsonResult(data.workflows.map((w) => ({
17
+ id: w.id,
18
+ name: w.name,
19
+ path: w.path,
20
+ state: w.state,
21
+ html_url: w.html_url,
22
+ })));
23
+ }
24
+ catch (e) {
25
+ return errorResult(e);
26
+ }
27
+ });
28
+ server.tool("github_list_workflow_runs", "List workflow runs (filter by workflow id, branch, or status).", {
29
+ owner,
30
+ repo,
31
+ workflow_id: z.number().optional().describe("Workflow ID"),
32
+ branch: z.string().optional(),
33
+ status: workflowStatus,
34
+ per_page: perPage,
35
+ }, async ({ owner, repo, workflow_id, branch, status, per_page }) => {
36
+ try {
37
+ const octokit = getOctokit();
38
+ const common = {
39
+ owner,
40
+ repo,
41
+ branch: branch ?? undefined,
42
+ status: status,
43
+ per_page: per_page ?? 20,
44
+ };
45
+ const { data } = workflow_id
46
+ ? await octokit.rest.actions.listWorkflowRuns({
47
+ ...common,
48
+ workflow_id,
49
+ })
50
+ : await octokit.rest.actions.listWorkflowRunsForRepo(common);
51
+ return jsonResult(data.workflow_runs.map((r) => ({
52
+ id: r.id,
53
+ name: r.name,
54
+ status: r.status,
55
+ conclusion: r.conclusion,
56
+ head_branch: r.head_branch,
57
+ head_sha: r.head_sha,
58
+ event: r.event,
59
+ html_url: r.html_url,
60
+ created_at: r.created_at,
61
+ updated_at: r.updated_at,
62
+ })));
63
+ }
64
+ catch (e) {
65
+ return errorResult(e);
66
+ }
67
+ });
68
+ server.tool("github_get_workflow_run", "Get details for a single workflow run.", {
69
+ owner,
70
+ repo,
71
+ run_id: z.number(),
72
+ }, async ({ owner, repo, run_id }) => {
73
+ try {
74
+ const { data } = await getOctokit().rest.actions.getWorkflowRun({
75
+ owner,
76
+ repo,
77
+ run_id,
78
+ });
79
+ return jsonResult({
80
+ id: data.id,
81
+ name: data.name,
82
+ status: data.status,
83
+ conclusion: data.conclusion,
84
+ head_branch: data.head_branch,
85
+ head_sha: data.head_sha,
86
+ html_url: data.html_url,
87
+ jobs_url: data.jobs_url,
88
+ run_attempt: data.run_attempt,
89
+ });
90
+ }
91
+ catch (e) {
92
+ return errorResult(e);
93
+ }
94
+ });
95
+ server.tool("github_list_workflow_jobs", "List jobs for a workflow run (steps, conclusions).", {
96
+ owner,
97
+ repo,
98
+ run_id: z.number(),
99
+ }, async ({ owner, repo, run_id }) => {
100
+ try {
101
+ const { data } = await getOctokit().rest.actions.listJobsForWorkflowRun({
102
+ owner,
103
+ repo,
104
+ run_id,
105
+ per_page: 100,
106
+ });
107
+ return jsonResult(data.jobs.map((j) => ({
108
+ id: j.id,
109
+ name: j.name,
110
+ status: j.status,
111
+ conclusion: j.conclusion,
112
+ started_at: j.started_at,
113
+ completed_at: j.completed_at,
114
+ html_url: j.html_url,
115
+ steps: j.steps?.map((s) => ({
116
+ name: s.name,
117
+ status: s.status,
118
+ conclusion: s.conclusion,
119
+ })),
120
+ })));
121
+ }
122
+ catch (e) {
123
+ return errorResult(e);
124
+ }
125
+ });
126
+ server.tool("github_rerun_workflow", "Re-run a failed or cancelled workflow run.", {
127
+ owner,
128
+ repo,
129
+ run_id: z.number(),
130
+ }, async ({ owner, repo, run_id }) => {
131
+ try {
132
+ await getOctokit().rest.actions.reRunWorkflow({
133
+ owner,
134
+ repo,
135
+ run_id,
136
+ });
137
+ return jsonResult({ ok: true, run_id, message: "Workflow re-run requested" });
138
+ }
139
+ catch (e) {
140
+ return errorResult(e);
141
+ }
142
+ });
143
+ server.tool("github_cancel_workflow_run", "Cancel an in-progress workflow run.", {
144
+ owner,
145
+ repo,
146
+ run_id: z.number(),
147
+ }, async ({ owner, repo, run_id }) => {
148
+ try {
149
+ await getOctokit().rest.actions.cancelWorkflowRun({
150
+ owner,
151
+ repo,
152
+ run_id,
153
+ });
154
+ return jsonResult({ ok: true, run_id, message: "Workflow cancel requested" });
155
+ }
156
+ catch (e) {
157
+ return errorResult(e);
158
+ }
159
+ });
160
+ server.tool("github_dispatch_workflow", "Trigger a workflow_dispatch event (manual CI run).", {
161
+ owner,
162
+ repo,
163
+ workflow_id: z.string().describe("Workflow ID or filename e.g. deploy.yml"),
164
+ ref: z.string().describe("Branch or tag to run on"),
165
+ inputs: z
166
+ .string()
167
+ .optional()
168
+ .describe("JSON object of workflow input key/value pairs"),
169
+ }, async ({ owner, repo, workflow_id, ref, inputs }) => {
170
+ try {
171
+ const parsedInputs = inputs ? JSON.parse(inputs) : undefined;
172
+ await getOctokit().rest.actions.createWorkflowDispatch({
173
+ owner,
174
+ repo,
175
+ workflow_id,
176
+ ref,
177
+ inputs: parsedInputs,
178
+ });
179
+ return jsonResult({ ok: true, workflow_id, ref, message: "workflow_dispatch sent" });
180
+ }
181
+ catch (e) {
182
+ return errorResult(e);
183
+ }
184
+ });
185
+ server.tool("github_list_actions_secrets", "List Actions secret names (values are never returned).", { owner, repo }, async ({ owner, repo }) => {
186
+ try {
187
+ const { data } = await getOctokit().rest.actions.listRepoSecrets({
188
+ owner,
189
+ repo,
190
+ per_page: 100,
191
+ });
192
+ return jsonResult(data.secrets.map((s) => ({
193
+ name: s.name,
194
+ updated_at: s.updated_at,
195
+ })));
196
+ }
197
+ catch (e) {
198
+ return errorResult(e);
199
+ }
200
+ });
201
+ server.tool("github_list_actions_variables", "List Actions variables for a repository (non-secret config).", { owner, repo }, async ({ owner, repo }) => {
202
+ try {
203
+ const { data } = await getOctokit().rest.actions.listRepoVariables({
204
+ owner,
205
+ repo,
206
+ per_page: 100,
207
+ });
208
+ return jsonResult(data.variables);
209
+ }
210
+ catch (e) {
211
+ return errorResult(e);
212
+ }
213
+ });
214
+ server.tool("github_list_workflow_run_artifacts", "List CI build artifacts for a workflow run.", {
215
+ owner,
216
+ repo,
217
+ run_id: z.number(),
218
+ }, async ({ owner, repo, run_id }) => {
219
+ try {
220
+ const { data } = await getOctokit().rest.actions.listWorkflowRunArtifacts({
221
+ owner,
222
+ repo,
223
+ run_id,
224
+ per_page: 100,
225
+ });
226
+ return jsonResult(data.artifacts.map((a) => ({
227
+ id: a.id,
228
+ name: a.name,
229
+ size_in_bytes: a.size_in_bytes,
230
+ expired: a.expired,
231
+ expires_at: a.expires_at,
232
+ archive_download_url: a.archive_download_url,
233
+ })));
234
+ }
235
+ catch (e) {
236
+ return errorResult(e);
237
+ }
238
+ });
239
+ server.tool("github_download_workflow_artifact", "Get download URL metadata for a workflow artifact (follow URL with auth to download zip).", {
240
+ owner,
241
+ repo,
242
+ artifact_id: z.number(),
243
+ }, async ({ owner, repo, artifact_id }) => {
244
+ try {
245
+ const { data } = await getOctokit().rest.actions.downloadArtifact({
246
+ owner,
247
+ repo,
248
+ artifact_id,
249
+ archive_format: "zip",
250
+ });
251
+ return jsonResult(data);
252
+ }
253
+ catch (e) {
254
+ return errorResult(e);
255
+ }
256
+ });
257
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerBillingTools(server: McpServer): void;