github-pro-mcp 0.0.2 → 0.0.4

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.
Files changed (43) hide show
  1. package/LICENSE +76 -0
  2. package/README.md +196 -69
  3. package/THIRD-PARTY-NOTICES.md +635 -0
  4. package/dist-package/index.js +28661 -0
  5. package/package.json +23 -17
  6. package/dist/client.d.ts +0 -28
  7. package/dist/client.js +0 -85
  8. package/dist/index.d.ts +0 -2
  9. package/dist/index.js +0 -70
  10. package/dist/tools/issue_fields.d.ts +0 -4
  11. package/dist/tools/issue_fields.js +0 -108
  12. package/dist/tools/issues.d.ts +0 -12
  13. package/dist/tools/issues.js +0 -634
  14. package/dist/tools/labels.d.ts +0 -3
  15. package/dist/tools/labels.js +0 -87
  16. package/dist/tools/organizations.d.ts +0 -4
  17. package/dist/tools/organizations.js +0 -89
  18. package/dist/tools/projects.d.ts +0 -4
  19. package/dist/tools/projects.js +0 -440
  20. package/dist/tools/pull_requests.d.ts +0 -4
  21. package/dist/tools/pull_requests.js +0 -127
  22. package/dist/tools/repos.d.ts +0 -9
  23. package/dist/tools/repos.js +0 -251
  24. package/dist/tools/server.d.ts +0 -14
  25. package/dist/tools/server.js +0 -37
  26. package/dist/types/github-api.d.ts +0 -63
  27. package/dist/types/github-api.js +0 -1
  28. package/dist/utils/circuitBreaker.d.ts +0 -11
  29. package/dist/utils/circuitBreaker.js +0 -125
  30. package/dist/utils/errors.d.ts +0 -17
  31. package/dist/utils/errors.js +0 -107
  32. package/dist/utils/logger.d.ts +0 -14
  33. package/dist/utils/logger.js +0 -40
  34. package/dist/utils/metrics.d.ts +0 -11
  35. package/dist/utils/metrics.js +0 -70
  36. package/dist/utils/rateLimit.d.ts +0 -21
  37. package/dist/utils/rateLimit.js +0 -40
  38. package/dist/utils/retry.d.ts +0 -9
  39. package/dist/utils/retry.js +0 -76
  40. package/dist/utils/types.d.ts +0 -33
  41. package/dist/utils/types.js +0 -33
  42. package/dist/utils/validation.d.ts +0 -13
  43. package/dist/utils/validation.js +0 -88
@@ -1,87 +0,0 @@
1
- import * as z from "zod/v4";
2
- import { createClient } from "../client.js";
3
- import { handleGitHubError } from "../utils/errors.js";
4
- import { validatePinnedOwnerRepo } from "../utils/validation.js";
5
- import { OwnerRepoSchema, PaginationSchema } from "../utils/types.js";
6
- export function registerLabelList(server) {
7
- server.registerTool("label_list", {
8
- description: "List labels in a GitHub repository.",
9
- inputSchema: OwnerRepoSchema.extend({
10
- page: PaginationSchema.shape.page,
11
- per_page: PaginationSchema.shape.per_page,
12
- }),
13
- }, async ({ owner, repo, page, per_page }) => {
14
- try {
15
- const pinErr = validatePinnedOwnerRepo(owner, repo);
16
- if (pinErr)
17
- return pinErr;
18
- const octokit = createClient();
19
- const { data } = await octokit.rest.issues.listLabelsForRepo({ owner, repo, page, per_page });
20
- return {
21
- content: [{ type: "text", text: JSON.stringify(data.map((l) => ({
22
- name: l.name,
23
- color: l.color,
24
- description: l.description,
25
- default: l.default,
26
- })), null, 2) }],
27
- };
28
- }
29
- catch (err) {
30
- return handleGitHubError(err);
31
- }
32
- });
33
- }
34
- export function registerLabelWrite(server) {
35
- server.registerTool("label_write", {
36
- description: "Create, update, or delete a label in a GitHub repository. Method 'create' requires name and color. Method 'update' requires name (current name) and optional color/description/new_name. Method 'delete' requires name only.",
37
- inputSchema: OwnerRepoSchema.extend({
38
- method: z.enum(["create", "update", "delete"]).describe("Action to perform on the label"),
39
- name: z.string().min(1).describe("Label name (for update: current name)"),
40
- color: z.string().regex(/^[0-9a-fA-F]{6}$/).optional().describe("6-character hex color without # (required for create)"),
41
- description: z.string().optional().describe("Label description"),
42
- new_name: z.string().optional().describe("New name for rename (update only)"),
43
- }),
44
- }, async ({ owner, repo, method, name, color, description, new_name }) => {
45
- try {
46
- const pinErr = validatePinnedOwnerRepo(owner, repo);
47
- if (pinErr)
48
- return pinErr;
49
- const octokit = createClient();
50
- if (method === "create") {
51
- if (!color) {
52
- return {
53
- content: [{ type: "text", text: "color is required for creating a label (6-char hex without #)" }],
54
- isError: true,
55
- };
56
- }
57
- const { data } = await octokit.rest.issues.createLabel({ owner, repo, name, color, description });
58
- return {
59
- content: [{ type: "text", text: JSON.stringify({
60
- name: data.name,
61
- color: data.color,
62
- description: data.description,
63
- }, null, 2) }],
64
- };
65
- }
66
- else if (method === "update") {
67
- const { data } = await octokit.rest.issues.updateLabel({ owner, repo, name, color, description, new_name });
68
- return {
69
- content: [{ type: "text", text: JSON.stringify({
70
- name: data.name,
71
- color: data.color,
72
- description: data.description,
73
- }, null, 2) }],
74
- };
75
- }
76
- else {
77
- await octokit.rest.issues.deleteLabel({ owner, repo, name });
78
- return {
79
- content: [{ type: "text", text: JSON.stringify({ success: true, deleted: name }, null, 2) }],
80
- };
81
- }
82
- }
83
- catch (err) {
84
- return handleGitHubError(err);
85
- }
86
- });
87
- }
@@ -1,4 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export declare function registerOrganizationSearch(server: McpServer): void;
3
- export declare function registerOwnerListIssueType(server: McpServer): void;
4
- export declare function registerOwnerListOrganization(server: McpServer): void;
@@ -1,89 +0,0 @@
1
- import * as z from "zod/v4";
2
- import { createClient, getWebBaseUrl } from "../client.js";
3
- import { handleGitHubError } from "../utils/errors.js";
4
- import { PaginationSchema } from "../utils/types.js";
5
- import { requireOrg, requireUser, validatePinnedOwnerRepo } from "../utils/validation.js";
6
- export function registerOrganizationSearch(server) {
7
- server.registerTool("organization_search", {
8
- description: "Search for organizations on GitHub by query. Supports qualifiers like 'location:...', 'repos:>...'.",
9
- inputSchema: z.object({
10
- query: z.string().min(1).max(1000).describe("Search query, e.g., 'microsoft' or 'location:seattle repos:>5'"),
11
- page: PaginationSchema.shape.page,
12
- per_page: PaginationSchema.shape.per_page,
13
- }),
14
- }, async ({ query, page, per_page }) => {
15
- try {
16
- const octokit = createClient();
17
- const { data } = await octokit.rest.search.users({ q: `${query}+type:org`, page, per_page });
18
- return {
19
- content: [{ type: "text", text: JSON.stringify({
20
- total_count: data.total_count,
21
- items: data.items.map((u) => {
22
- const item = u;
23
- return {
24
- login: item.login,
25
- description: item.description ?? null,
26
- html_url: item.html_url,
27
- };
28
- }),
29
- }, null, 2) }],
30
- };
31
- }
32
- catch (err) {
33
- return handleGitHubError(err);
34
- }
35
- });
36
- }
37
- export function registerOwnerListIssueType(server) {
38
- server.registerTool("owner_list_issue_type", {
39
- description: "List issue types for an organization (e.g., Bug, Feature, Enhancement). Owner must be an organization.",
40
- inputSchema: z.object({
41
- owner: z.string().min(1).describe("Organization owner"),
42
- }),
43
- }, async ({ owner }) => {
44
- try {
45
- const pinErr = validatePinnedOwnerRepo(owner);
46
- if (pinErr)
47
- return pinErr;
48
- const octokit = createClient();
49
- const orgErr = await requireOrg(owner);
50
- if (orgErr)
51
- return orgErr;
52
- const { data } = await octokit.request('GET /orgs/{owner}/issue-types', { owner });
53
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
54
- }
55
- catch (err) {
56
- return handleGitHubError(err);
57
- }
58
- });
59
- }
60
- export function registerOwnerListOrganization(server) {
61
- server.registerTool("owner_list_organization", {
62
- description: "List all organizations that a user account belongs to. Provide a username, not an organization name.",
63
- inputSchema: z.object({
64
- owner: z.string().min(1).describe("Username to list organizations for"),
65
- }),
66
- }, async ({ owner }) => {
67
- try {
68
- const pinErr = validatePinnedOwnerRepo(owner);
69
- if (pinErr)
70
- return pinErr;
71
- const octokit = createClient();
72
- const userErr = await requireUser(owner);
73
- if (userErr)
74
- return userErr;
75
- const { data } = await octokit.rest.orgs.listForUser({ username: owner });
76
- return {
77
- content: [{ type: "text", text: JSON.stringify(data.map((o) => ({
78
- login: o.login,
79
- description: o.description,
80
- avatar_url: o.avatar_url,
81
- html_url: `${getWebBaseUrl()}/${o.login}`,
82
- })), null, 2) }],
83
- };
84
- }
85
- catch (err) {
86
- return handleGitHubError(err);
87
- }
88
- });
89
- }
@@ -1,4 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export declare function registerOwnerListProject(server: McpServer): void;
3
- export declare function registerProjectsGet(server: McpServer): void;
4
- export declare function registerProjectsWrite(server: McpServer): void;
@@ -1,440 +0,0 @@
1
- import * as z from "zod/v4";
2
- import { gql } from "../client.js";
3
- import { handleGitHubError } from "../utils/errors.js";
4
- import { validatePinnedOwnerRepo } from "../utils/validation.js";
5
- export function registerOwnerListProject(server) {
6
- server.registerTool("owner_list_project", {
7
- description: "List GitHub Projects (v2) for an owner. Works for both organizations and individual users.",
8
- inputSchema: z.object({
9
- owner: z.string().min(1).describe("Organization or user login"),
10
- owner_type: z.enum(["org", "user"]).describe("Type of owner: 'org' for organization, 'user' for individual user"),
11
- state: z.enum(["open", "closed"]).optional().describe("Filter by project state"),
12
- first: z.number().int().min(1).max(100).optional().default(30).describe("Results per page (max 100)"),
13
- after: z.string().optional().describe("Cursor for pagination (endCursor from previous response)"),
14
- }),
15
- }, async ({ owner, owner_type, state, first, after }) => {
16
- try {
17
- const pinErr = validatePinnedOwnerRepo(owner);
18
- if (pinErr)
19
- return pinErr;
20
- const rootType = owner_type === "org" ? "organization" : "user";
21
- const gqlQuery = `
22
- query($owner: String!, $first: Int!, $after: String, $searchStr: String) {
23
- ${rootType}(login: $owner) {
24
- projectsV2(first: $first, after: $after, query: $searchStr) {
25
- nodes {
26
- id
27
- number
28
- title
29
- shortDescription
30
- url
31
- closed
32
- createdAt
33
- updatedAt
34
- }
35
- pageInfo {
36
- hasNextPage
37
- endCursor
38
- }
39
- }
40
- }
41
- }
42
- `;
43
- const data = await gql(gqlQuery, {
44
- owner, first, after: after ?? null, searchStr: state ? `is:${state}` : null,
45
- });
46
- const root = owner_type === "org"
47
- ? data.organization
48
- : data.user;
49
- if (!root) {
50
- const label = owner_type === "org" ? "Organization" : "User";
51
- return {
52
- content: [{ type: "text", text: `${label} "${owner}" not found.` }],
53
- isError: true,
54
- };
55
- }
56
- const projects = root.projectsV2?.nodes ?? [];
57
- const pageInfo = root.projectsV2?.pageInfo;
58
- return {
59
- content: [{ type: "text", text: JSON.stringify({
60
- projects: projects.map((p) => ({
61
- id: p.id,
62
- number: p.number,
63
- title: p.title,
64
- description: p.shortDescription,
65
- url: p.url,
66
- closed: p.closed,
67
- created_at: p.createdAt,
68
- updated_at: p.updatedAt,
69
- })),
70
- page_info: {
71
- has_next_page: pageInfo?.hasNextPage ?? false,
72
- end_cursor: pageInfo?.endCursor ?? null,
73
- },
74
- }, null, 2) }],
75
- };
76
- }
77
- catch (err) {
78
- return handleGitHubError(err);
79
- }
80
- });
81
- }
82
- function formatFieldValue(fv) {
83
- if (fv.name !== undefined)
84
- return { field: fv.field?.name, value: fv.name };
85
- if (fv.text !== undefined)
86
- return { field: fv.field?.name, value: fv.text };
87
- if (fv.number !== undefined)
88
- return { field: fv.field?.name, value: fv.number };
89
- if (fv.date !== undefined)
90
- return { field: fv.field?.name, value: fv.date };
91
- if (fv.title !== undefined)
92
- return { field: fv.field?.name, value: fv.title };
93
- return { value: null };
94
- }
95
- function formatProjectItem(item) {
96
- return {
97
- id: item.id,
98
- type: item.type,
99
- content: item.content ? {
100
- number: item.content.number,
101
- title: item.content.title,
102
- state: item.content.state,
103
- url: item.content.url,
104
- assignees: item.content.assignees?.nodes?.map((a) => a.login) ?? [],
105
- } : null,
106
- field_values: item.fieldValues?.nodes?.map(formatFieldValue) ?? [],
107
- };
108
- }
109
- export function registerProjectsGet(server) {
110
- server.registerTool("projects_get", {
111
- description: "Get a single GitHub Project (v2) by its GraphQL node ID with optional fields, items, and status updates. Each include_* flag makes additional API calls.",
112
- inputSchema: z.object({
113
- project_id: z.string().min(1).describe("GraphQL node ID of the project (obtain from owner_list_project)"),
114
- include_fields: z.boolean().optional().default(false).describe("Include project field definitions"),
115
- include_items: z.boolean().optional().default(false).describe("Include project items (issues/PRs)"),
116
- include_status_updates: z.boolean().optional().default(false).describe("Include status updates"),
117
- max_fields: z.number().int().positive().optional().default(20).describe("Maximum number of project field definitions to return (default: 20)"),
118
- max_items: z.number().int().positive().optional().default(50).describe("Maximum number of project items to return (default: 50)"),
119
- max_status_updates: z.number().int().positive().optional().default(20).describe("Maximum number of status updates to return (default: 20)"),
120
- max_field_values: z.number().int().positive().optional().default(20).describe("Maximum number of field values per item (default: 20)"),
121
- }),
122
- }, async ({ project_id, include_fields, include_items, include_status_updates, max_fields, max_items, max_status_updates, max_field_values }) => {
123
- try {
124
- const fieldsLimit = max_fields ?? 20;
125
- const itemsLimit = max_items ?? 50;
126
- const statusUpdatesLimit = max_status_updates ?? 20;
127
- const fieldValueLimit = max_field_values ?? 20;
128
- const query = `
129
- query(
130
- $id: ID!,
131
- $includeFields: Boolean!,
132
- $includeItems: Boolean!,
133
- $includeStatusUpdates: Boolean!,
134
- $fieldsLimit: Int!,
135
- $itemsLimit: Int!,
136
- $statusUpdatesLimit: Int!,
137
- $fieldValueLimit: Int!
138
- ) {
139
- node(id: $id) {
140
- ... on ProjectV2 {
141
- id
142
- number
143
- title
144
- shortDescription
145
- url
146
- closed
147
- closedAt
148
- createdAt
149
- updatedAt
150
- fields(first: $fieldsLimit) @include(if: $includeFields) {
151
- nodes {
152
- ... on ProjectV2Field {
153
- id name dataType
154
- }
155
- ... on ProjectV2SingleSelectField {
156
- id name dataType
157
- options { id name color }
158
- }
159
- ... on ProjectV2IterationField {
160
- id name dataType
161
- configuration { iterations { id title startDate duration } }
162
- }
163
- }
164
- }
165
- items(first: $itemsLimit) @include(if: $includeItems) {
166
- nodes {
167
- id
168
- type
169
- fieldValues(first: $fieldValueLimit) {
170
- nodes {
171
- ... on ProjectV2ItemFieldSingleSelectValue {
172
- name optionId field { ... on ProjectV2SingleSelectField { id name } }
173
- }
174
- ... on ProjectV2ItemFieldTextValue {
175
- text field { ... on ProjectV2Field { id name } }
176
- }
177
- ... on ProjectV2ItemFieldNumberValue {
178
- number field { ... on ProjectV2Field { id name } }
179
- }
180
- ... on ProjectV2ItemFieldDateValue {
181
- date field { ... on ProjectV2Field { id name } }
182
- }
183
- ... on ProjectV2ItemFieldIterationValue {
184
- title field { ... on ProjectV2IterationField { id name } }
185
- }
186
- }
187
- }
188
- content {
189
- ... on Issue { number title state url assignees(first: 5) { nodes { login } } }
190
- ... on PullRequest { number title state url }
191
- }
192
- }
193
- }
194
- statusUpdates(first: $statusUpdatesLimit) @include(if: $includeStatusUpdates) {
195
- nodes {
196
- id
197
- body
198
- status
199
- createdAt
200
- updatedAt
201
- startDate
202
- targetDate
203
- }
204
- }
205
- }
206
- }
207
- }
208
- `;
209
- const data = await gql(query, {
210
- id: project_id,
211
- includeFields: !!include_fields,
212
- includeItems: !!include_items,
213
- includeStatusUpdates: !!include_status_updates,
214
- fieldsLimit,
215
- itemsLimit,
216
- statusUpdatesLimit,
217
- fieldValueLimit,
218
- });
219
- const project = data.node;
220
- if (!project) {
221
- return {
222
- content: [{ type: "text", text: `Project not found. Check the project_id is a valid GraphQL node ID.` }],
223
- isError: true,
224
- };
225
- }
226
- const result = {
227
- id: project.id,
228
- number: project.number,
229
- title: project.title,
230
- description: project.shortDescription,
231
- url: project.url,
232
- closed: project.closed,
233
- closed_at: project.closedAt,
234
- created_at: project.createdAt,
235
- updated_at: project.updatedAt,
236
- };
237
- if (include_fields) {
238
- result.fields = project.fields?.nodes ?? [];
239
- }
240
- if (include_items) {
241
- result.items = (project.items?.nodes ?? []).map(formatProjectItem);
242
- }
243
- if (include_status_updates) {
244
- result.status_updates = project.statusUpdates?.nodes ?? [];
245
- }
246
- return {
247
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
248
- };
249
- }
250
- catch (err) {
251
- return handleGitHubError(err);
252
- }
253
- });
254
- }
255
- export function registerProjectsWrite(server) {
256
- server.registerTool("projects_write", {
257
- description: "Manage items in a GitHub Project (v2): add, update field values, delete items, or create status updates. Methods: add_item (requires content_id), update_item (requires item_id + field_values; field_values each need field_id, value, and value_type), delete_item (requires item_id), create_status_update (requires body; optional status/start_date/target_date). Note: update_item sends field values one at a time — if one fails, earlier updates are already committed. Check partial: true in the response.",
258
- inputSchema: z.object({
259
- project_id: z.string().min(1).describe("GraphQL node ID of the project"),
260
- method: z.enum(["add_item", "update_item", "delete_item", "create_status_update"]).describe("Action to perform"),
261
- content_id: z.string().optional().describe("GraphQL node ID of issue/PR to add (required for add_item)"),
262
- issue_number: z.number().int().positive().optional().describe("Issue number (alternative to content_id — auto-resolves to node ID)"),
263
- item_id: z.string().optional().describe("GraphQL node ID of the project item (required for update_item, delete_item)"),
264
- field_values: z.array(z.object({
265
- field_id: z.string().describe("GraphQL node ID of the project field"),
266
- value: z.union([z.string(), z.number(), z.null()]).describe("Field value"),
267
- value_type: z.enum(["single_select", "text", "number", "date", "iteration", "title"]).describe("Type of the field value — use the field's dataType from projects_get / owner_list_project to determine the correct value_type"),
268
- })).optional().describe("Field values to set (update_item)"),
269
- body: z.string().optional().describe("Status update body text (create_status_update)"),
270
- status: z.string().optional().describe("Status update status (create_status_update) — pass any value GitHub accepts"),
271
- start_date: z.string().optional().describe("Start date in ISO 8601 format (create_status_update)"),
272
- target_date: z.string().optional().describe("Target date in ISO 8601 format (create_status_update)"),
273
- owner: z.string().optional().describe("Required when using issue_number. Must match the server's configured owner if pinned."),
274
- repo: z.string().optional().describe("Required when using issue_number. Must match the server's configured repository if pinned."),
275
- }),
276
- }, async ({ project_id, method, content_id, issue_number, item_id, field_values, body, status, start_date, target_date, owner, repo }) => {
277
- try {
278
- if (method === "add_item") {
279
- let resolvedContentId = content_id;
280
- if (!resolvedContentId && issue_number) {
281
- if (!owner || !repo) {
282
- return {
283
- content: [{ type: "text", text: "owner and repo are required when using issue_number." }],
284
- isError: true,
285
- };
286
- }
287
- const pinErr = validatePinnedOwnerRepo(owner, repo);
288
- if (pinErr)
289
- return pinErr;
290
- const idQuery = `
291
- query($owner: String!, $repo: String!, $number: Int!) {
292
- repository(owner: $owner, name: $repo) {
293
- issue(number: $number) { id }
294
- }
295
- }
296
- `;
297
- const idData = await gql(idQuery, { owner, repo, number: issue_number });
298
- resolvedContentId = idData.repository?.issue?.id;
299
- if (!resolvedContentId) {
300
- return {
301
- content: [{ type: "text", text: `Could not resolve issue #${issue_number} in ${owner}/${repo}.` }],
302
- isError: true,
303
- };
304
- }
305
- }
306
- if (!resolvedContentId) {
307
- return {
308
- content: [{ type: "text", text: "content_id or issue_number is required for add_item." }],
309
- isError: true,
310
- };
311
- }
312
- const mutation = `
313
- mutation($projectId: ID!, $contentId: ID!) {
314
- addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
315
- item { id }
316
- }
317
- }
318
- `;
319
- const result = await gql(mutation, { projectId: project_id, contentId: resolvedContentId });
320
- return {
321
- content: [{ type: "text", text: JSON.stringify({
322
- success: true,
323
- item_id: result.addProjectV2ItemById?.item?.id,
324
- }, null, 2) }],
325
- };
326
- }
327
- else if (method === "update_item") {
328
- if (!item_id || !field_values || field_values.length === 0) {
329
- return {
330
- content: [{ type: "text", text: "item_id and field_values are required for update_item." }],
331
- isError: true,
332
- };
333
- }
334
- const results = [];
335
- for (const fv of field_values) {
336
- const mutation = `
337
- mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $value: ProjectV2FieldValue!) {
338
- updateProjectV2ItemFieldValue(input: {
339
- projectId: $projectId, itemId: $itemId, fieldId: $fieldId, value: $value
340
- }) {
341
- projectV2Item { id }
342
- }
343
- }
344
- `;
345
- let value;
346
- switch (fv.value_type) {
347
- case "single_select":
348
- value = fv.value === null ? { singleSelectOptionId: null } : { singleSelectOptionId: String(fv.value) };
349
- break;
350
- case "date":
351
- value = fv.value === null ? { date: null } : { date: fv.value };
352
- break;
353
- case "text":
354
- value = fv.value === null ? { text: null } : { text: String(fv.value) };
355
- break;
356
- case "number":
357
- value = fv.value === null ? { number: null } : { number: Number(fv.value) };
358
- break;
359
- case "iteration":
360
- value = fv.value === null ? { iterationId: null } : { iterationId: String(fv.value) };
361
- break;
362
- case "title":
363
- value = fv.value === null ? { title: null } : { title: String(fv.value) };
364
- break;
365
- default:
366
- if (fv.value === null) {
367
- return {
368
- content: [{ type: "text", text: `Cannot clear field — unknown value_type "${fv.value_type}". Provide a valid value_type or use a known type (single_select, text, number, date, iteration, title).` }],
369
- isError: true,
370
- };
371
- }
372
- value = { text: String(fv.value) };
373
- }
374
- const result = await gql(mutation, {
375
- projectId: project_id, itemId: item_id, fieldId: fv.field_id, value,
376
- });
377
- results.push({
378
- field_id: fv.field_id,
379
- result: result.updateProjectV2ItemFieldValue?.projectV2Item?.id ? "updated" : "failed",
380
- });
381
- }
382
- return {
383
- content: [{ type: "text", text: JSON.stringify({
384
- success: results.every((r) => r.result === "updated"),
385
- updates: results,
386
- }, null, 2) }],
387
- };
388
- }
389
- else if (method === "delete_item") {
390
- if (!item_id) {
391
- return {
392
- content: [{ type: "text", text: "item_id is required for delete_item." }],
393
- isError: true,
394
- };
395
- }
396
- const mutation = `
397
- mutation($projectId: ID!, $itemId: ID!) {
398
- deleteProjectV2Item(input: { projectId: $projectId, itemId: $itemId }) {
399
- deletedItemId
400
- }
401
- }
402
- `;
403
- const result = await gql(mutation, { projectId: project_id, itemId: item_id });
404
- return {
405
- content: [{ type: "text", text: JSON.stringify({
406
- success: true,
407
- deleted_item_id: result.deleteProjectV2Item?.deletedItemId,
408
- }, null, 2) }],
409
- };
410
- }
411
- else if (method === "create_status_update") {
412
- const mutation = `
413
- mutation($projectId: ID!, $body: String!, $status: ProjectV2StatusUpdateStatus, $startDate: Date, $targetDate: Date) {
414
- createProjectV2StatusUpdate(input: {
415
- projectId: $projectId, body: $body, status: $status, startDate: $startDate, targetDate: $targetDate
416
- }) {
417
- statusUpdate { id body status createdAt }
418
- }
419
- }
420
- `;
421
- const result = await gql(mutation, {
422
- projectId: project_id, body: body ?? "", status: status ?? null, startDate: start_date ?? null, targetDate: target_date ?? null,
423
- });
424
- return {
425
- content: [{ type: "text", text: JSON.stringify({
426
- success: true,
427
- status_update: result.createProjectV2StatusUpdate?.statusUpdate,
428
- }, null, 2) }],
429
- };
430
- }
431
- return {
432
- content: [{ type: "text", text: `Unknown method: ${method}` }],
433
- isError: true,
434
- };
435
- }
436
- catch (err) {
437
- return handleGitHubError(err);
438
- }
439
- });
440
- }
@@ -1,4 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export declare function registerPullRequestRead(server: McpServer): void;
3
- export declare function registerPullRequestCreate(server: McpServer): void;
4
- export declare function registerPullRequestMerge(server: McpServer): void;