mcp-server-github-actions 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ofer Shapira
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 ADDED
@@ -0,0 +1,127 @@
1
+ # mcp-server-github-actions
2
+
3
+ [![npm version](https://img.shields.io/npm/v/mcp-server-github-actions.svg)](https://www.npmjs.com/package/mcp-server-github-actions)
4
+ [![npm downloads](https://img.shields.io/npm/dm/mcp-server-github-actions.svg)](https://www.npmjs.com/package/mcp-server-github-actions)
5
+ [![CI](https://github.com/ofershap/mcp-server-github-actions/actions/workflows/ci.yml/badge.svg)](https://github.com/ofershap/mcp-server-github-actions/actions/workflows/ci.yml)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ Manage GitHub Actions workflows from your AI assistant. List runs, read logs, re-run failed jobs, cancel builds, and trigger deployments without leaving your editor.
10
+
11
+ ```bash
12
+ npx mcp-server-github-actions
13
+ ```
14
+
15
+ > Works with Claude Desktop, Cursor, VS Code Copilot, and any MCP client. Requires a GitHub token with Actions permissions.
16
+
17
+ ![MCP server for GitHub Actions workflows, CI/CD runs, and deployment logs](assets/demo.gif)
18
+
19
+ <sub>Demo built with <a href="https://github.com/ofershap/remotion-readme-kit">remotion-readme-kit</a></sub>
20
+
21
+ ## Why
22
+
23
+ GitHub's official MCP server covers repos, issues, and PRs, but it doesn't touch Actions. That means when your CI fails, you still have to open a browser, find the run, click through to the logs, and figure out what went wrong. This server fills that gap. You can ask your assistant "why did the last CI run fail?" or "re-run the failed jobs" and get answers right where you're working. It uses the same GitHub REST API you'd use manually, just without the context switching.
24
+
25
+ ## Tools
26
+
27
+ | Tool | Description |
28
+ | ------------------- | ------------------------------------------------------------ |
29
+ | `list_workflows` | List all workflow files in a repository |
30
+ | `list_runs` | List workflow runs (optionally filter by workflow or status) |
31
+ | `get_run` | Get details of a specific workflow run |
32
+ | `get_run_logs` | Get the logs URL for a run (zip file download) |
33
+ | `rerun_workflow` | Re-run an entire workflow run |
34
+ | `rerun_failed_jobs` | Re-run only the failed jobs from a run |
35
+ | `cancel_run` | Cancel an in-progress or queued run |
36
+ | `list_artifacts` | List artifacts produced by a workflow run |
37
+ | `trigger_workflow` | Trigger a workflow via `workflow_dispatch` |
38
+
39
+ ## Quick Start
40
+
41
+ ### Cursor
42
+
43
+ Add to your Cursor MCP settings (e.g. `~/.cursor/mcp.json` or project-level):
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "github-actions": {
49
+ "command": "npx",
50
+ "args": ["-y", "mcp-server-github-actions"],
51
+ "env": {
52
+ "GITHUB_TOKEN": "<your-token>"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### Claude Desktop
60
+
61
+ Add to `claude_desktop_config.json`:
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "github-actions": {
67
+ "command": "npx",
68
+ "args": ["-y", "mcp-server-github-actions"],
69
+ "env": {
70
+ "GITHUB_TOKEN": "<your-token>"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### VS Code
78
+
79
+ Configure your MCP client to run:
80
+
81
+ ```bash
82
+ npx mcp-server-github-actions
83
+ ```
84
+
85
+ Make sure `GITHUB_TOKEN` is set in the environment (e.g. in your shell profile or client config).
86
+
87
+ ## Auth
88
+
89
+ Create a GitHub Personal Access Token:
90
+
91
+ 1. **Settings** > **Developer settings** > **Personal access tokens**
92
+ 2. Choose **Fine-grained tokens** (recommended) or **Tokens (classic)**
93
+ 3. Fine-grained: select your repos, then enable **Actions: Read and Write**
94
+ 4. Classic: enable the `repo` scope (includes Actions)
95
+
96
+ ## Example Prompts
97
+
98
+ - "List the last 5 workflow runs for ofershap/mcp-server-docker"
99
+ - "Show me the workflows in the microsoft/vscode repo"
100
+ - "Get details for run 12345 in owner/repo"
101
+ - "Re-run the failed jobs for run 67890 in my-org/my-repo"
102
+ - "Cancel the currently running workflow run 11111"
103
+ - "List artifacts from the latest run in owner/repo"
104
+ - "Trigger the deploy.yml workflow on the staging branch for my-org/my-app"
105
+ - "What's the status of the most recent CI run for this project?"
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ npm install
111
+ npm run typecheck
112
+ npm run build
113
+ npm test
114
+ npm run lint
115
+ npm run format
116
+ ```
117
+
118
+ ## Author
119
+
120
+ **Ofer Shapira**
121
+
122
+ [![LinkedIn](https://img.shields.io/badge/LinkedIn-ofershap-blue?logo=linkedin)](https://linkedin.com/in/ofershap)
123
+ [![GitHub](https://img.shields.io/badge/GitHub-ofershap-black?logo=github)](https://github.com/ofershap)
124
+
125
+ ## License
126
+
127
+ MIT © 2026 Ofer Shapira
package/dist/index.js ADDED
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+
8
+ // src/github-actions.ts
9
+ var API_BASE = "https://api.github.com";
10
+ async function ghFetch(token, path, options = {}) {
11
+ const response = await fetch(`${API_BASE}${path}`, {
12
+ method: options.method ?? "GET",
13
+ headers: {
14
+ Authorization: `Bearer ${token}`,
15
+ Accept: "application/vnd.github+json",
16
+ "X-GitHub-Api-Version": "2022-11-28",
17
+ ...options.body ? { "Content-Type": "application/json" } : {}
18
+ },
19
+ body: options.body ? JSON.stringify(options.body) : void 0
20
+ });
21
+ if (!response.ok) {
22
+ const text = await response.text();
23
+ throw new Error(`GitHub API error ${response.status}: ${text}`);
24
+ }
25
+ if (response.status === 204) return {};
26
+ return response.json();
27
+ }
28
+ async function listWorkflows(token, owner, repo) {
29
+ const data = await ghFetch(
30
+ token,
31
+ `/repos/${owner}/${repo}/actions/workflows`
32
+ );
33
+ return data.workflows;
34
+ }
35
+ async function listRuns(token, owner, repo, workflowId, status, perPage = 10) {
36
+ const params = new URLSearchParams({ per_page: String(perPage) });
37
+ if (status) params.set("status", status);
38
+ const path = workflowId ? `/repos/${owner}/${repo}/actions/workflows/${workflowId}/runs?${params}` : `/repos/${owner}/${repo}/actions/runs?${params}`;
39
+ const data = await ghFetch(token, path);
40
+ return data.workflow_runs;
41
+ }
42
+ async function getRun(token, owner, repo, runId) {
43
+ return ghFetch(
44
+ token,
45
+ `/repos/${owner}/${repo}/actions/runs/${runId}`
46
+ );
47
+ }
48
+ async function getRunLogs(token, owner, repo, runId) {
49
+ const response = await fetch(
50
+ `${API_BASE}/repos/${owner}/${repo}/actions/runs/${runId}/logs`,
51
+ {
52
+ headers: {
53
+ Authorization: `Bearer ${token}`,
54
+ Accept: "application/vnd.github+json",
55
+ "X-GitHub-Api-Version": "2022-11-28"
56
+ },
57
+ redirect: "follow"
58
+ }
59
+ );
60
+ if (!response.ok) {
61
+ throw new Error(
62
+ `GitHub API error ${response.status}: ${await response.text()}`
63
+ );
64
+ }
65
+ return `Logs URL: ${response.url}
66
+ (GitHub returns a redirect to a zip file \u2014 download it to inspect logs)`;
67
+ }
68
+ async function rerunWorkflow(token, owner, repo, runId) {
69
+ await ghFetch(token, `/repos/${owner}/${repo}/actions/runs/${runId}/rerun`, {
70
+ method: "POST"
71
+ });
72
+ return `Workflow run ${runId} re-run triggered`;
73
+ }
74
+ async function rerunFailedJobs(token, owner, repo, runId) {
75
+ await ghFetch(
76
+ token,
77
+ `/repos/${owner}/${repo}/actions/runs/${runId}/rerun-failed-jobs`,
78
+ { method: "POST" }
79
+ );
80
+ return `Failed jobs for run ${runId} re-run triggered`;
81
+ }
82
+ async function cancelRun(token, owner, repo, runId) {
83
+ await ghFetch(token, `/repos/${owner}/${repo}/actions/runs/${runId}/cancel`, {
84
+ method: "POST"
85
+ });
86
+ return `Workflow run ${runId} cancelled`;
87
+ }
88
+ async function listArtifacts(token, owner, repo, runId) {
89
+ const data = await ghFetch(
90
+ token,
91
+ `/repos/${owner}/${repo}/actions/runs/${runId}/artifacts`
92
+ );
93
+ return data.artifacts;
94
+ }
95
+ async function triggerWorkflow(token, owner, repo, workflowId, ref, inputs) {
96
+ await ghFetch(
97
+ token,
98
+ `/repos/${owner}/${repo}/actions/workflows/${workflowId}/dispatches`,
99
+ {
100
+ method: "POST",
101
+ body: { ref, inputs: inputs ?? {} }
102
+ }
103
+ );
104
+ return `Workflow ${workflowId} triggered on ${ref}`;
105
+ }
106
+
107
+ // src/index.ts
108
+ var server = new McpServer({
109
+ name: "mcp-server-github-actions",
110
+ version: "1.0.0"
111
+ });
112
+ function getToken() {
113
+ const token = process.env.GITHUB_TOKEN;
114
+ if (!token) {
115
+ throw new Error(
116
+ "GITHUB_TOKEN environment variable is required. Create a token at https://github.com/settings/tokens"
117
+ );
118
+ }
119
+ return token;
120
+ }
121
+ server.tool(
122
+ "list_workflows",
123
+ "List all workflow files in a GitHub repository.",
124
+ {
125
+ owner: z.string().describe("Repository owner (username or org)"),
126
+ repo: z.string().describe("Repository name")
127
+ },
128
+ async ({ owner, repo }) => {
129
+ const token = getToken();
130
+ const workflows = await listWorkflows(token, owner, repo);
131
+ const text = workflows.length === 0 ? "No workflows found." : workflows.map((w) => `id: ${w.id} name: ${w.name} state: ${w.state}`).join("\n");
132
+ return { content: [{ type: "text", text }] };
133
+ }
134
+ );
135
+ server.tool(
136
+ "list_runs",
137
+ "List workflow runs for a repository. Optionally filter by workflow or status.",
138
+ {
139
+ owner: z.string().describe("Repository owner (username or org)"),
140
+ repo: z.string().describe("Repository name"),
141
+ workflow_id: z.number().optional().describe("Workflow ID to filter runs (from list_workflows)"),
142
+ status: z.string().optional().describe("Filter by status: completed, in_progress, queued, etc."),
143
+ per_page: z.number().optional().default(10).describe("Number of runs to return (default 10)")
144
+ },
145
+ async ({ owner, repo, workflow_id, status, per_page }) => {
146
+ const token = getToken();
147
+ const runs = await listRuns(
148
+ token,
149
+ owner,
150
+ repo,
151
+ workflow_id,
152
+ status,
153
+ per_page
154
+ );
155
+ const text = runs.length === 0 ? "No runs found." : runs.map(
156
+ (r) => `#${r.run_number} ${r.name} status: ${r.status} conclusion: ${r.conclusion ?? "\u2014"} branch: ${r.head_branch} ${r.html_url}`
157
+ ).join("\n");
158
+ return { content: [{ type: "text", text }] };
159
+ }
160
+ );
161
+ server.tool(
162
+ "get_run",
163
+ "Get details of a specific workflow run.",
164
+ {
165
+ owner: z.string().describe("Repository owner (username or org)"),
166
+ repo: z.string().describe("Repository name"),
167
+ run_id: z.number().describe("Workflow run ID")
168
+ },
169
+ async ({ owner, repo, run_id }) => {
170
+ const token = getToken();
171
+ const run = await getRun(token, owner, repo, run_id);
172
+ const text = [
173
+ `Run #${run.run_number}: ${run.name}`,
174
+ `Status: ${run.status} Conclusion: ${run.conclusion ?? "\u2014"}`,
175
+ `Branch: ${run.head_branch}`,
176
+ `URL: ${run.html_url}`,
177
+ `Created: ${run.created_at} Updated: ${run.updated_at}`
178
+ ].join("\n");
179
+ return { content: [{ type: "text", text }] };
180
+ }
181
+ );
182
+ server.tool(
183
+ "get_run_logs",
184
+ "Get logs URL for a workflow run. GitHub returns a redirect to a zip file.",
185
+ {
186
+ owner: z.string().describe("Repository owner (username or org)"),
187
+ repo: z.string().describe("Repository name"),
188
+ run_id: z.number().describe("Workflow run ID")
189
+ },
190
+ async ({ owner, repo, run_id }) => {
191
+ const token = getToken();
192
+ const logs = await getRunLogs(token, owner, repo, run_id);
193
+ return { content: [{ type: "text", text: logs }] };
194
+ }
195
+ );
196
+ server.tool(
197
+ "rerun_workflow",
198
+ "Re-run a complete workflow run.",
199
+ {
200
+ owner: z.string().describe("Repository owner (username or org)"),
201
+ repo: z.string().describe("Repository name"),
202
+ run_id: z.number().describe("Workflow run ID")
203
+ },
204
+ async ({ owner, repo, run_id }) => {
205
+ const token = getToken();
206
+ const result = await rerunWorkflow(token, owner, repo, run_id);
207
+ return { content: [{ type: "text", text: result }] };
208
+ }
209
+ );
210
+ server.tool(
211
+ "rerun_failed_jobs",
212
+ "Re-run only the failed jobs from a workflow run.",
213
+ {
214
+ owner: z.string().describe("Repository owner (username or org)"),
215
+ repo: z.string().describe("Repository name"),
216
+ run_id: z.number().describe("Workflow run ID")
217
+ },
218
+ async ({ owner, repo, run_id }) => {
219
+ const token = getToken();
220
+ const result = await rerunFailedJobs(token, owner, repo, run_id);
221
+ return { content: [{ type: "text", text: result }] };
222
+ }
223
+ );
224
+ server.tool(
225
+ "cancel_run",
226
+ "Cancel a workflow run that is in progress or queued.",
227
+ {
228
+ owner: z.string().describe("Repository owner (username or org)"),
229
+ repo: z.string().describe("Repository name"),
230
+ run_id: z.number().describe("Workflow run ID")
231
+ },
232
+ async ({ owner, repo, run_id }) => {
233
+ const token = getToken();
234
+ const result = await cancelRun(token, owner, repo, run_id);
235
+ return { content: [{ type: "text", text: result }] };
236
+ }
237
+ );
238
+ server.tool(
239
+ "list_artifacts",
240
+ "List artifacts produced by a workflow run.",
241
+ {
242
+ owner: z.string().describe("Repository owner (username or org)"),
243
+ repo: z.string().describe("Repository name"),
244
+ run_id: z.number().describe("Workflow run ID")
245
+ },
246
+ async ({ owner, repo, run_id }) => {
247
+ const token = getToken();
248
+ const artifacts = await listArtifacts(token, owner, repo, run_id);
249
+ const text = artifacts.length === 0 ? "No artifacts found." : artifacts.map(
250
+ (a) => `name: ${a.name} size: ${a.size_in_bytes} bytes expired: ${a.expired ? "yes" : "no"}`
251
+ ).join("\n");
252
+ return { content: [{ type: "text", text }] };
253
+ }
254
+ );
255
+ server.tool(
256
+ "trigger_workflow",
257
+ "Trigger a workflow run via workflow_dispatch. Requires a workflow with workflow_dispatch enabled.",
258
+ {
259
+ owner: z.string().describe("Repository owner (username or org)"),
260
+ repo: z.string().describe("Repository name"),
261
+ workflow_id: z.string().describe("Workflow ID or filename (e.g. ci.yml)"),
262
+ ref: z.string().optional().default("main").describe("Branch or tag to run on (default: main)"),
263
+ inputs: z.record(z.string()).optional().describe("Optional workflow inputs as key-value pairs")
264
+ },
265
+ async ({ owner, repo, workflow_id, ref, inputs }) => {
266
+ const token = getToken();
267
+ const result = await triggerWorkflow(
268
+ token,
269
+ owner,
270
+ repo,
271
+ workflow_id,
272
+ ref,
273
+ inputs
274
+ );
275
+ return { content: [{ type: "text", text: result }] };
276
+ }
277
+ );
278
+ async function main() {
279
+ const transport = new StdioServerTransport();
280
+ await server.connect(transport);
281
+ }
282
+ main().catch((error) => {
283
+ console.error("Fatal error:", error);
284
+ process.exit(1);
285
+ });
286
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/github-actions.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport {\n listWorkflows,\n listRuns,\n getRun,\n getRunLogs,\n rerunWorkflow,\n rerunFailedJobs,\n cancelRun,\n listArtifacts,\n triggerWorkflow,\n} from \"./github-actions.js\";\n\nconst server = new McpServer({\n name: \"mcp-server-github-actions\",\n version: \"1.0.0\",\n});\n\nfunction getToken(): string {\n const token = process.env.GITHUB_TOKEN;\n if (!token) {\n throw new Error(\n \"GITHUB_TOKEN environment variable is required. Create a token at https://github.com/settings/tokens\",\n );\n }\n return token;\n}\n\nserver.tool(\n \"list_workflows\",\n \"List all workflow files in a GitHub repository.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n },\n async ({ owner, repo }) => {\n const token = getToken();\n const workflows = await listWorkflows(token, owner, repo);\n const text =\n workflows.length === 0\n ? \"No workflows found.\"\n : workflows\n .map((w) => `id: ${w.id} name: ${w.name} state: ${w.state}`)\n .join(\"\\n\");\n return { content: [{ type: \"text\", text }] };\n },\n);\n\nserver.tool(\n \"list_runs\",\n \"List workflow runs for a repository. Optionally filter by workflow or status.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n workflow_id: z\n .number()\n .optional()\n .describe(\"Workflow ID to filter runs (from list_workflows)\"),\n status: z\n .string()\n .optional()\n .describe(\"Filter by status: completed, in_progress, queued, etc.\"),\n per_page: z\n .number()\n .optional()\n .default(10)\n .describe(\"Number of runs to return (default 10)\"),\n },\n async ({ owner, repo, workflow_id, status, per_page }) => {\n const token = getToken();\n const runs = await listRuns(\n token,\n owner,\n repo,\n workflow_id,\n status,\n per_page,\n );\n const text =\n runs.length === 0\n ? \"No runs found.\"\n : runs\n .map(\n (r) =>\n `#${r.run_number} ${r.name} status: ${r.status} conclusion: ${r.conclusion ?? \"—\"} branch: ${r.head_branch} ${r.html_url}`,\n )\n .join(\"\\n\");\n return { content: [{ type: \"text\", text }] };\n },\n);\n\nserver.tool(\n \"get_run\",\n \"Get details of a specific workflow run.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n run_id: z.number().describe(\"Workflow run ID\"),\n },\n async ({ owner, repo, run_id }) => {\n const token = getToken();\n const run = await getRun(token, owner, repo, run_id);\n const text = [\n `Run #${run.run_number}: ${run.name}`,\n `Status: ${run.status} Conclusion: ${run.conclusion ?? \"—\"}`,\n `Branch: ${run.head_branch}`,\n `URL: ${run.html_url}`,\n `Created: ${run.created_at} Updated: ${run.updated_at}`,\n ].join(\"\\n\");\n return { content: [{ type: \"text\", text }] };\n },\n);\n\nserver.tool(\n \"get_run_logs\",\n \"Get logs URL for a workflow run. GitHub returns a redirect to a zip file.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n run_id: z.number().describe(\"Workflow run ID\"),\n },\n async ({ owner, repo, run_id }) => {\n const token = getToken();\n const logs = await getRunLogs(token, owner, repo, run_id);\n return { content: [{ type: \"text\", text: logs }] };\n },\n);\n\nserver.tool(\n \"rerun_workflow\",\n \"Re-run a complete workflow run.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n run_id: z.number().describe(\"Workflow run ID\"),\n },\n async ({ owner, repo, run_id }) => {\n const token = getToken();\n const result = await rerunWorkflow(token, owner, repo, run_id);\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nserver.tool(\n \"rerun_failed_jobs\",\n \"Re-run only the failed jobs from a workflow run.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n run_id: z.number().describe(\"Workflow run ID\"),\n },\n async ({ owner, repo, run_id }) => {\n const token = getToken();\n const result = await rerunFailedJobs(token, owner, repo, run_id);\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nserver.tool(\n \"cancel_run\",\n \"Cancel a workflow run that is in progress or queued.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n run_id: z.number().describe(\"Workflow run ID\"),\n },\n async ({ owner, repo, run_id }) => {\n const token = getToken();\n const result = await cancelRun(token, owner, repo, run_id);\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nserver.tool(\n \"list_artifacts\",\n \"List artifacts produced by a workflow run.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n run_id: z.number().describe(\"Workflow run ID\"),\n },\n async ({ owner, repo, run_id }) => {\n const token = getToken();\n const artifacts = await listArtifacts(token, owner, repo, run_id);\n const text =\n artifacts.length === 0\n ? \"No artifacts found.\"\n : artifacts\n .map(\n (a) =>\n `name: ${a.name} size: ${a.size_in_bytes} bytes expired: ${a.expired ? \"yes\" : \"no\"}`,\n )\n .join(\"\\n\");\n return { content: [{ type: \"text\", text }] };\n },\n);\n\nserver.tool(\n \"trigger_workflow\",\n \"Trigger a workflow run via workflow_dispatch. Requires a workflow with workflow_dispatch enabled.\",\n {\n owner: z.string().describe(\"Repository owner (username or org)\"),\n repo: z.string().describe(\"Repository name\"),\n workflow_id: z.string().describe(\"Workflow ID or filename (e.g. ci.yml)\"),\n ref: z\n .string()\n .optional()\n .default(\"main\")\n .describe(\"Branch or tag to run on (default: main)\"),\n inputs: z\n .record(z.string())\n .optional()\n .describe(\"Optional workflow inputs as key-value pairs\"),\n },\n async ({ owner, repo, workflow_id, ref, inputs }) => {\n const token = getToken();\n const result = await triggerWorkflow(\n token,\n owner,\n repo,\n workflow_id,\n ref,\n inputs,\n );\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n});\n","const API_BASE = \"https://api.github.com\";\n\ninterface RequestOptions {\n method?: string;\n body?: unknown;\n}\n\nasync function ghFetch<T>(\n token: string,\n path: string,\n options: RequestOptions = {},\n): Promise<T> {\n const response = await fetch(`${API_BASE}${path}`, {\n method: options.method ?? \"GET\",\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n ...(options.body ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`GitHub API error ${response.status}: ${text}`);\n }\n if (response.status === 204) return {} as T;\n return response.json() as Promise<T>;\n}\n\nexport interface Workflow {\n id: number;\n name: string;\n path: string;\n state: string;\n}\n\nexport interface WorkflowRun {\n id: number;\n name: string;\n status: string;\n conclusion: string | null;\n head_branch: string;\n html_url: string;\n created_at: string;\n updated_at: string;\n run_number: number;\n}\n\nexport interface Artifact {\n id: number;\n name: string;\n size_in_bytes: number;\n created_at: string;\n expired: boolean;\n}\n\nexport async function listWorkflows(\n token: string,\n owner: string,\n repo: string,\n): Promise<Workflow[]> {\n const data = await ghFetch<{ workflows: Workflow[] }>(\n token,\n `/repos/${owner}/${repo}/actions/workflows`,\n );\n return data.workflows;\n}\n\nexport async function listRuns(\n token: string,\n owner: string,\n repo: string,\n workflowId?: number,\n status?: string,\n perPage = 10,\n): Promise<WorkflowRun[]> {\n const params = new URLSearchParams({ per_page: String(perPage) });\n if (status) params.set(\"status\", status);\n const path = workflowId\n ? `/repos/${owner}/${repo}/actions/workflows/${workflowId}/runs?${params}`\n : `/repos/${owner}/${repo}/actions/runs?${params}`;\n const data = await ghFetch<{ workflow_runs: WorkflowRun[] }>(token, path);\n return data.workflow_runs;\n}\n\nexport async function getRun(\n token: string,\n owner: string,\n repo: string,\n runId: number,\n): Promise<WorkflowRun> {\n return ghFetch<WorkflowRun>(\n token,\n `/repos/${owner}/${repo}/actions/runs/${runId}`,\n );\n}\n\nexport async function getRunLogs(\n token: string,\n owner: string,\n repo: string,\n runId: number,\n): Promise<string> {\n const response = await fetch(\n `${API_BASE}/repos/${owner}/${repo}/actions/runs/${runId}/logs`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n redirect: \"follow\",\n },\n );\n if (!response.ok) {\n throw new Error(\n `GitHub API error ${response.status}: ${await response.text()}`,\n );\n }\n return `Logs URL: ${response.url}\\n(GitHub returns a redirect to a zip file — download it to inspect logs)`;\n}\n\nexport async function rerunWorkflow(\n token: string,\n owner: string,\n repo: string,\n runId: number,\n): Promise<string> {\n await ghFetch(token, `/repos/${owner}/${repo}/actions/runs/${runId}/rerun`, {\n method: \"POST\",\n });\n return `Workflow run ${runId} re-run triggered`;\n}\n\nexport async function rerunFailedJobs(\n token: string,\n owner: string,\n repo: string,\n runId: number,\n): Promise<string> {\n await ghFetch(\n token,\n `/repos/${owner}/${repo}/actions/runs/${runId}/rerun-failed-jobs`,\n { method: \"POST\" },\n );\n return `Failed jobs for run ${runId} re-run triggered`;\n}\n\nexport async function cancelRun(\n token: string,\n owner: string,\n repo: string,\n runId: number,\n): Promise<string> {\n await ghFetch(token, `/repos/${owner}/${repo}/actions/runs/${runId}/cancel`, {\n method: \"POST\",\n });\n return `Workflow run ${runId} cancelled`;\n}\n\nexport async function listArtifacts(\n token: string,\n owner: string,\n repo: string,\n runId: number,\n): Promise<Artifact[]> {\n const data = await ghFetch<{ artifacts: Artifact[] }>(\n token,\n `/repos/${owner}/${repo}/actions/runs/${runId}/artifacts`,\n );\n return data.artifacts;\n}\n\nexport async function triggerWorkflow(\n token: string,\n owner: string,\n repo: string,\n workflowId: number | string,\n ref: string,\n inputs?: Record<string, string>,\n): Promise<string> {\n await ghFetch(\n token,\n `/repos/${owner}/${repo}/actions/workflows/${workflowId}/dispatches`,\n {\n method: \"POST\",\n body: { ref, inputs: inputs ?? {} },\n },\n );\n return `Workflow ${workflowId} triggered on ${ref}`;\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "mcp-server-github-actions",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for GitHub Actions — view workflow runs, read logs, re-run failed jobs, and manage CI/CD from your IDE",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcp-server-github-actions": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "typecheck": "tsc --noEmit",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "test:coverage": "vitest run --coverage",
18
+ "lint": "eslint . && prettier --check .",
19
+ "format": "prettier --write .",
20
+ "prepare": "husky"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "mcp-server",
25
+ "model-context-protocol",
26
+ "github",
27
+ "github-actions",
28
+ "ci-cd",
29
+ "workflows",
30
+ "ci",
31
+ "cd",
32
+ "ai",
33
+ "llm",
34
+ "claude",
35
+ "cursor"
36
+ ],
37
+ "author": "Ofer Shapira",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/ofershap/mcp-server-github-actions.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/ofershap/mcp-server-github-actions/issues"
45
+ },
46
+ "homepage": "https://github.com/ofershap/mcp-server-github-actions#readme",
47
+ "dependencies": {
48
+ "@modelcontextprotocol/sdk": "^1.26.0",
49
+ "zod": "^3.25.0"
50
+ },
51
+ "devDependencies": {
52
+ "@eslint/js": "^9.0.0",
53
+ "@types/node": "^22.0.0",
54
+ "eslint": "^9.0.0",
55
+ "eslint-config-prettier": "^10.0.0",
56
+ "husky": "^9.0.0",
57
+ "lint-staged": "^15.0.0",
58
+ "prettier": "^3.0.0",
59
+ "tsup": "^8.0.0",
60
+ "typescript": "^5.7.0",
61
+ "typescript-eslint": "^8.0.0",
62
+ "vitest": "^3.2.0"
63
+ },
64
+ "lint-staged": {
65
+ "*.{ts,tsx,js}": "eslint --fix",
66
+ "*.{json,md,yml,yaml}": "prettier --write"
67
+ }
68
+ }