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.
@@ -0,0 +1,182 @@
1
+ import { z } from "zod";
2
+ import { getOctokit } from "../lib/github.js";
3
+ import { owner, perPage, repo } from "../lib/fields.js";
4
+ import { errorResult, jsonResult } from "../lib/response.js";
5
+ export function registerRepoTools(server) {
6
+ server.tool("github_get_authenticated_user", "Get the authenticated GitHub user (validates token and shows login).", {}, async () => {
7
+ try {
8
+ const { data } = await getOctokit().rest.users.getAuthenticated();
9
+ return jsonResult({
10
+ login: data.login,
11
+ id: data.id,
12
+ name: data.name,
13
+ type: data.type,
14
+ html_url: data.html_url,
15
+ });
16
+ }
17
+ catch (e) {
18
+ return errorResult(e);
19
+ }
20
+ });
21
+ server.tool("github_get_rate_limit", "Check REST API rate limit status for the current token.", {}, async () => {
22
+ try {
23
+ const { data } = await getOctokit().rest.rateLimit.get();
24
+ return jsonResult(data);
25
+ }
26
+ catch (e) {
27
+ return errorResult(e);
28
+ }
29
+ });
30
+ server.tool("github_create_repository", "Create a new repository for the authenticated user or an organization.", {
31
+ name: z.string().describe("Repository name"),
32
+ description: z.string().optional().describe("Short description"),
33
+ private: z.boolean().optional().describe("Private repo (default false)"),
34
+ org: z.string().optional().describe("Organization login; omit for user account"),
35
+ auto_init: z.boolean().optional().describe("Initialize with README (default true)"),
36
+ gitignore_template: z.string().optional().describe("Gitignore template name, e.g. Node"),
37
+ }, async ({ name, description, private: isPrivate, org, auto_init, gitignore_template }) => {
38
+ try {
39
+ const octokit = getOctokit();
40
+ const body = {
41
+ name,
42
+ description: description ?? undefined,
43
+ private: isPrivate ?? false,
44
+ auto_init: auto_init ?? true,
45
+ gitignore_template: gitignore_template ?? undefined,
46
+ };
47
+ const { data } = org
48
+ ? await octokit.rest.repos.createInOrg({ org, ...body })
49
+ : await octokit.rest.repos.createForAuthenticatedUser(body);
50
+ return jsonResult({
51
+ full_name: data.full_name,
52
+ html_url: data.html_url,
53
+ clone_url: data.clone_url,
54
+ default_branch: data.default_branch,
55
+ private: data.private,
56
+ });
57
+ }
58
+ catch (e) {
59
+ return errorResult(e);
60
+ }
61
+ });
62
+ server.tool("github_get_repository", "Get repository metadata.", { owner, repo }, async ({ owner, repo }) => {
63
+ try {
64
+ const { data } = await getOctokit().rest.repos.get({ owner, repo });
65
+ return jsonResult({
66
+ full_name: data.full_name,
67
+ description: data.description,
68
+ default_branch: data.default_branch,
69
+ html_url: data.html_url,
70
+ clone_url: data.clone_url,
71
+ private: data.private,
72
+ open_issues: data.open_issues_count,
73
+ pushed_at: data.pushed_at,
74
+ });
75
+ }
76
+ catch (e) {
77
+ return errorResult(e);
78
+ }
79
+ });
80
+ server.tool("github_list_repositories", "List repositories for a user or organization.", {
81
+ owner,
82
+ type: z
83
+ .string()
84
+ .optional()
85
+ .describe("For orgs: all | public | private | forks | sources | member"),
86
+ per_page: perPage,
87
+ }, async ({ owner, type, per_page }) => {
88
+ try {
89
+ const octokit = getOctokit();
90
+ const { data: user } = await octokit.rest.users.getByUsername({ username: owner });
91
+ let repos;
92
+ if (user.type === "Organization") {
93
+ const res = await octokit.rest.repos.listForOrg({
94
+ org: owner,
95
+ type: type ?? "all",
96
+ per_page: per_page ?? 30,
97
+ });
98
+ repos = res.data;
99
+ }
100
+ else {
101
+ const res = await octokit.rest.repos.listForUser({
102
+ username: owner,
103
+ per_page: per_page ?? 30,
104
+ });
105
+ repos = res.data;
106
+ }
107
+ return jsonResult(repos.map((r) => ({
108
+ full_name: r.full_name,
109
+ private: r.private,
110
+ default_branch: r.default_branch,
111
+ html_url: r.html_url,
112
+ })));
113
+ }
114
+ catch (e) {
115
+ return errorResult(e);
116
+ }
117
+ });
118
+ server.tool("github_create_branch", "Create a branch from an existing ref (e.g. main).", {
119
+ owner,
120
+ repo,
121
+ branch: z.string().describe("New branch name"),
122
+ from_ref: z
123
+ .string()
124
+ .optional()
125
+ .describe("Source ref (branch or SHA). Default: default branch"),
126
+ }, async ({ owner, repo, branch, from_ref }) => {
127
+ try {
128
+ const octokit = getOctokit();
129
+ let sha;
130
+ if (from_ref) {
131
+ const ref = await octokit.rest.git.getRef({
132
+ owner,
133
+ repo,
134
+ ref: `heads/${from_ref}`,
135
+ });
136
+ sha = ref.data.object.sha;
137
+ }
138
+ else {
139
+ const { data: repoData } = await octokit.rest.repos.get({ owner, repo });
140
+ const ref = await octokit.rest.git.getRef({
141
+ owner,
142
+ repo,
143
+ ref: `heads/${repoData.default_branch}`,
144
+ });
145
+ sha = ref.data.object.sha;
146
+ }
147
+ const { data } = await octokit.rest.git.createRef({
148
+ owner,
149
+ repo,
150
+ ref: `refs/heads/${branch}`,
151
+ sha,
152
+ });
153
+ return jsonResult({ ref: data.ref, url: data.url });
154
+ }
155
+ catch (e) {
156
+ return errorResult(e);
157
+ }
158
+ });
159
+ server.tool("github_fork_repository", "Fork a repository to the authenticated user or an organization.", {
160
+ owner,
161
+ repo,
162
+ organization: z.string().optional().describe("Org to fork into; default: your user"),
163
+ default_branch_only: z.boolean().optional().describe("Fork only default branch"),
164
+ }, async ({ owner, repo, organization, default_branch_only }) => {
165
+ try {
166
+ const { data } = await getOctokit().rest.repos.createFork({
167
+ owner,
168
+ repo,
169
+ organization: organization ?? undefined,
170
+ default_branch_only: default_branch_only ?? undefined,
171
+ });
172
+ return jsonResult({
173
+ full_name: data.full_name,
174
+ html_url: data.html_url,
175
+ clone_url: data.clone_url,
176
+ });
177
+ }
178
+ catch (e) {
179
+ return errorResult(e);
180
+ }
181
+ });
182
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerWebhookTools(server: McpServer): void;
3
+ export declare function registerCollaboratorTools(server: McpServer): void;
@@ -0,0 +1,132 @@
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
+ export function registerWebhookTools(server) {
6
+ server.tool("github_list_webhooks", "List repository webhooks.", { owner, repo, per_page: perPage }, async ({ owner, repo, per_page }) => {
7
+ try {
8
+ const { data } = await getOctokit().rest.repos.listWebhooks({
9
+ owner,
10
+ repo,
11
+ per_page: per_page ?? 30,
12
+ });
13
+ return jsonResult(data.map((h) => ({
14
+ id: h.id,
15
+ name: h.name,
16
+ active: h.active,
17
+ events: h.events,
18
+ url: h.config.url,
19
+ })));
20
+ }
21
+ catch (e) {
22
+ return errorResult(e);
23
+ }
24
+ });
25
+ server.tool("github_create_webhook", "Create a repository webhook (e.g. for CI notifications).", {
26
+ owner,
27
+ repo,
28
+ url: z.string().describe("Payload URL"),
29
+ secret: z.string().optional().describe("Webhook secret"),
30
+ events: z
31
+ .string()
32
+ .optional()
33
+ .describe('JSON array of events, default ["push","pull_request"]'),
34
+ active: z.boolean().optional(),
35
+ }, async ({ owner, repo, url, secret, events, active }) => {
36
+ try {
37
+ const eventList = events
38
+ ? JSON.parse(events)
39
+ : ["push", "pull_request"];
40
+ const { data } = await getOctokit().rest.repos.createWebhook({
41
+ owner,
42
+ repo,
43
+ config: {
44
+ url,
45
+ content_type: "json",
46
+ secret: secret ?? undefined,
47
+ insecure_ssl: "0",
48
+ },
49
+ events: eventList,
50
+ active: active ?? true,
51
+ });
52
+ return jsonResult({ id: data.id, url: data.config.url, events: data.events });
53
+ }
54
+ catch (e) {
55
+ return errorResult(e);
56
+ }
57
+ });
58
+ server.tool("github_delete_webhook", "Delete a repository webhook by ID.", {
59
+ owner,
60
+ repo,
61
+ hook_id: z.number(),
62
+ }, async ({ owner, repo, hook_id }) => {
63
+ try {
64
+ await getOctokit().rest.repos.deleteWebhook({ owner, repo, hook_id });
65
+ return jsonResult({ ok: true, hook_id });
66
+ }
67
+ catch (e) {
68
+ return errorResult(e);
69
+ }
70
+ });
71
+ }
72
+ export function registerCollaboratorTools(server) {
73
+ server.tool("github_list_collaborators", "List collaborators and their permissions.", { owner, repo, per_page: perPage }, async ({ owner, repo, per_page }) => {
74
+ try {
75
+ const { data } = await getOctokit().rest.repos.listCollaborators({
76
+ owner,
77
+ repo,
78
+ per_page: per_page ?? 100,
79
+ });
80
+ return jsonResult(data.map((c) => ({
81
+ login: c.login,
82
+ permissions: c.permissions,
83
+ role_name: c.role_name,
84
+ })));
85
+ }
86
+ catch (e) {
87
+ return errorResult(e);
88
+ }
89
+ });
90
+ server.tool("github_add_collaborator", "Invite or add a collaborator to a repository.", {
91
+ owner,
92
+ repo,
93
+ username: z.string(),
94
+ permission: z
95
+ .string()
96
+ .optional()
97
+ .describe("pull | push | admin | maintain | triage (default push)"),
98
+ }, async ({ owner, repo, username, permission }) => {
99
+ try {
100
+ await getOctokit().rest.repos.addCollaborator({
101
+ owner,
102
+ repo,
103
+ username,
104
+ permission: permission,
105
+ });
106
+ return jsonResult({ ok: true, username, permission: permission ?? "push" });
107
+ }
108
+ catch (e) {
109
+ return errorResult(e);
110
+ }
111
+ });
112
+ server.tool("github_list_teams", "List teams in an organization (for CI/CD access planning).", {
113
+ org: z.string(),
114
+ per_page: perPage,
115
+ }, async ({ org, per_page }) => {
116
+ try {
117
+ const { data } = await getOctokit().rest.teams.list({
118
+ org,
119
+ per_page: per_page ?? 100,
120
+ });
121
+ return jsonResult(data.map((t) => ({
122
+ id: t.id,
123
+ name: t.name,
124
+ slug: t.slug,
125
+ privacy: t.privacy,
126
+ })));
127
+ }
128
+ catch (e) {
129
+ return errorResult(e);
130
+ }
131
+ });
132
+ }
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
- {
2
- "name": "github-cicd-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "github-cicd-mcp",
3
+ "version": "1.2.0",
4
+ "description": "MCP server for GitHub REST & GraphQL — repos, commits, PRs, issues, Actions CI/CD, releases, billing, and local git",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "LICENSE",
12
+ "CHANGELOG.md"
13
+ ],
14
+ "bin": {
15
+ "github-cicd-mcp": "./dist/index.js"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "start": "node dist/index.js",
20
+ "dev": "tsx src/index.ts",
21
+ "prepublishOnly": "npm run build",
22
+ "verify": "npm run build && node scripts/count-tools.mjs"
23
+ },
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "keywords": [
28
+ "mcp",
29
+ "model-context-protocol",
30
+ "github",
31
+ "ci-cd",
32
+ "devops",
33
+ "actions",
34
+ "pull-request"
35
+ ],
36
+ "author": "",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/devstephenke-sys/Github-Free-MCPs.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/devstephenke-sys/Github-Free-MCPs/issues"
44
+ },
45
+ "homepage": "https://github.com/devstephenke-sys/Github-Free-MCPs#readme",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "dependencies": {
50
+ "@modelcontextprotocol/sdk": "^1.32.1",
51
+ "@octokit/graphql": "^9.0.3",
52
+ "@octokit/rest": "^22.0.1",
53
+ "simple-git": "^3.27.0",
54
+ "zod": "^3.24.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^22.10.0",
58
+ "tsx": "^4.19.0",
59
+ "typescript": "^5.7.0"
60
+ }
61
+ }