Skip to content

Commit 16a69cf

Browse files
Copilotomgitsads
andcommitted
Migrate imports and first 3 tools (IssueRead, ListIssueTypes, helper functions)
Co-authored-by: omgitsads <4619+omgitsads@users.noreply.github.com>
1 parent 7480a09 commit 16a69cf

1 file changed

Lines changed: 97 additions & 80 deletions

File tree

pkg/github/issues.go

Lines changed: 97 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:build ignore
2-
31
package github
42

53
import (
@@ -15,10 +13,11 @@ import (
1513
"github.com/github/github-mcp-server/pkg/lockdown"
1614
"github.com/github/github-mcp-server/pkg/sanitize"
1715
"github.com/github/github-mcp-server/pkg/translations"
16+
"github.com/github/github-mcp-server/pkg/utils"
1817
"github.com/go-viper/mapstructure/v2"
1918
"github.com/google/go-github/v79/github"
20-
"github.com/mark3labs/mcp-go/mcp"
21-
"github.com/mark3labs/mcp-go/server"
19+
"github.com/google/jsonschema-go/jsonschema"
20+
"github.com/modelcontextprotocol/go-sdk/mcp"
2221
"github.com/shurcooL/githubv4"
2322
)
2423

@@ -229,85 +228,97 @@ func fragmentToIssue(fragment IssueFragment) *github.Issue {
229228
}
230229
}
231230

232-
// GetIssue creates a tool to get details of a specific issue in a GitHub repository.
233-
func IssueRead(getClient GetClientFn, getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc, flags FeatureFlags) (tool mcp.Tool, handler server.ToolHandlerFunc) {
234-
return mcp.NewTool("issue_read",
235-
mcp.WithDescription(t("TOOL_ISSUE_READ_DESCRIPTION", "Get information about a specific issue in a GitHub repository.")),
236-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
237-
Title: t("TOOL_ISSUE_READ_USER_TITLE", "Get issue details"),
238-
ReadOnlyHint: ToBoolPtr(true),
239-
}),
240-
mcp.WithString("method",
241-
mcp.Required(),
242-
mcp.Description(`The read operation to perform on a single issue.
231+
// IssueRead creates a tool to get details of a specific issue in a GitHub repository.
232+
func IssueRead(getClient GetClientFn, getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc, flags FeatureFlags) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) {
233+
schema := &jsonschema.Schema{
234+
Type: "object",
235+
Properties: map[string]*jsonschema.Schema{
236+
"method": {
237+
Type: "string",
238+
Description: `The read operation to perform on a single issue.
243239
Options are:
244240
1. get - Get details of a specific issue.
245241
2. get_comments - Get issue comments.
246242
3. get_sub_issues - Get sub-issues of the issue.
247243
4. get_labels - Get labels assigned to the issue.
248-
`),
244+
`,
245+
Enum: []any{"get", "get_comments", "get_sub_issues", "get_labels"},
246+
},
247+
"owner": {
248+
Type: "string",
249+
Description: "The owner of the repository",
250+
},
251+
"repo": {
252+
Type: "string",
253+
Description: "The name of the repository",
254+
},
255+
"issue_number": {
256+
Type: "number",
257+
Description: "The number of the issue",
258+
},
259+
},
260+
Required: []string{"method", "owner", "repo", "issue_number"},
261+
}
262+
WithPagination(schema)
249263

250-
mcp.Enum("get", "get_comments", "get_sub_issues", "get_labels"),
251-
),
252-
mcp.WithString("owner",
253-
mcp.Required(),
254-
mcp.Description("The owner of the repository"),
255-
),
256-
mcp.WithString("repo",
257-
mcp.Required(),
258-
mcp.Description("The name of the repository"),
259-
),
260-
mcp.WithNumber("issue_number",
261-
mcp.Required(),
262-
mcp.Description("The number of the issue"),
263-
),
264-
WithPagination(),
265-
),
266-
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
267-
method, err := RequiredParam[string](request, "method")
264+
return mcp.Tool{
265+
Name: "issue_read",
266+
Description: t("TOOL_ISSUE_READ_DESCRIPTION", "Get information about a specific issue in a GitHub repository."),
267+
Annotations: &mcp.ToolAnnotations{
268+
Title: t("TOOL_ISSUE_READ_USER_TITLE", "Get issue details"),
269+
ReadOnlyHint: true,
270+
},
271+
InputSchema: schema,
272+
},
273+
func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) {
274+
method, err := RequiredParam[string](args, "method")
268275
if err != nil {
269-
return mcp.NewToolResultError(err.Error()), nil
276+
return utils.NewToolResultError(err.Error()), nil, nil
270277
}
271278

272-
owner, err := RequiredParam[string](request, "owner")
279+
owner, err := RequiredParam[string](args, "owner")
273280
if err != nil {
274-
return mcp.NewToolResultError(err.Error()), nil
281+
return utils.NewToolResultError(err.Error()), nil, nil
275282
}
276-
repo, err := RequiredParam[string](request, "repo")
283+
repo, err := RequiredParam[string](args, "repo")
277284
if err != nil {
278-
return mcp.NewToolResultError(err.Error()), nil
285+
return utils.NewToolResultError(err.Error()), nil, nil
279286
}
280-
issueNumber, err := RequiredInt(request, "issue_number")
287+
issueNumber, err := RequiredInt(args, "issue_number")
281288
if err != nil {
282-
return mcp.NewToolResultError(err.Error()), nil
289+
return utils.NewToolResultError(err.Error()), nil, nil
283290
}
284291

285-
pagination, err := OptionalPaginationParams(request)
292+
pagination, err := OptionalPaginationParams(args)
286293
if err != nil {
287-
return mcp.NewToolResultError(err.Error()), nil
294+
return utils.NewToolResultError(err.Error()), nil, nil
288295
}
289296

290297
client, err := getClient(ctx)
291298
if err != nil {
292-
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
299+
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
293300
}
294301

295302
gqlClient, err := getGQLClient(ctx)
296303
if err != nil {
297-
return nil, fmt.Errorf("failed to get GitHub graphql client: %w", err)
304+
return utils.NewToolResultErrorFromErr("failed to get GitHub graphql client", err), nil, nil
298305
}
299306

300307
switch method {
301308
case "get":
302-
return GetIssue(ctx, client, gqlClient, owner, repo, issueNumber, flags)
309+
result, err := GetIssue(ctx, client, gqlClient, owner, repo, issueNumber, flags)
310+
return result, nil, err
303311
case "get_comments":
304-
return GetIssueComments(ctx, client, owner, repo, issueNumber, pagination, flags)
312+
result, err := GetIssueComments(ctx, client, owner, repo, issueNumber, pagination, flags)
313+
return result, nil, err
305314
case "get_sub_issues":
306-
return GetSubIssues(ctx, client, owner, repo, issueNumber, pagination, flags)
315+
result, err := GetSubIssues(ctx, client, owner, repo, issueNumber, pagination, flags)
316+
return result, nil, err
307317
case "get_labels":
308-
return GetIssueLabels(ctx, gqlClient, owner, repo, issueNumber, flags)
318+
result, err := GetIssueLabels(ctx, gqlClient, owner, repo, issueNumber, flags)
319+
return result, nil, err
309320
default:
310-
return mcp.NewToolResultError(fmt.Sprintf("unknown method: %s", method)), nil
321+
return utils.NewToolResultError(fmt.Sprintf("unknown method: %s", method)), nil, nil
311322
}
312323
}
313324
}
@@ -324,17 +335,17 @@ func GetIssue(ctx context.Context, client *github.Client, gqlClient *githubv4.Cl
324335
if err != nil {
325336
return nil, fmt.Errorf("failed to read response body: %w", err)
326337
}
327-
return mcp.NewToolResultError(fmt.Sprintf("failed to get issue: %s", string(body))), nil
338+
return utils.NewToolResultError(fmt.Sprintf("failed to get issue: %s", string(body))), nil
328339
}
329340

330341
if flags.LockdownMode {
331342
if issue.User != nil {
332343
shouldRemoveContent, err := lockdown.ShouldRemoveContent(ctx, gqlClient, *issue.User.Login, owner, repo)
333344
if err != nil {
334-
return mcp.NewToolResultError(fmt.Sprintf("failed to check lockdown mode: %v", err)), nil
345+
return utils.NewToolResultError(fmt.Sprintf("failed to check lockdown mode: %v", err)), nil
335346
}
336347
if shouldRemoveContent {
337-
return mcp.NewToolResultError("access to issue details is restricted by lockdown mode"), nil
348+
return utils.NewToolResultError("access to issue details is restricted by lockdown mode"), nil
338349
}
339350
}
340351
}
@@ -354,7 +365,7 @@ func GetIssue(ctx context.Context, client *github.Client, gqlClient *githubv4.Cl
354365
return nil, fmt.Errorf("failed to marshal issue: %w", err)
355366
}
356367

357-
return mcp.NewToolResultText(string(r)), nil
368+
return utils.NewToolResultText(string(r)), nil
358369
}
359370

360371
func GetIssueComments(ctx context.Context, client *github.Client, owner string, repo string, issueNumber int, pagination PaginationParams, _ FeatureFlags) (*mcp.CallToolResult, error) {
@@ -376,15 +387,15 @@ func GetIssueComments(ctx context.Context, client *github.Client, owner string,
376387
if err != nil {
377388
return nil, fmt.Errorf("failed to read response body: %w", err)
378389
}
379-
return mcp.NewToolResultError(fmt.Sprintf("failed to get issue comments: %s", string(body))), nil
390+
return utils.NewToolResultError(fmt.Sprintf("failed to get issue comments: %s", string(body))), nil
380391
}
381392

382393
r, err := json.Marshal(comments)
383394
if err != nil {
384395
return nil, fmt.Errorf("failed to marshal response: %w", err)
385396
}
386397

387-
return mcp.NewToolResultText(string(r)), nil
398+
return utils.NewToolResultText(string(r)), nil
388399
}
389400

390401
func GetSubIssues(ctx context.Context, client *github.Client, owner string, repo string, issueNumber int, pagination PaginationParams, _ FeatureFlags) (*mcp.CallToolResult, error) {
@@ -411,15 +422,15 @@ func GetSubIssues(ctx context.Context, client *github.Client, owner string, repo
411422
if err != nil {
412423
return nil, fmt.Errorf("failed to read response body: %w", err)
413424
}
414-
return mcp.NewToolResultError(fmt.Sprintf("failed to list sub-issues: %s", string(body))), nil
425+
return utils.NewToolResultError(fmt.Sprintf("failed to list sub-issues: %s", string(body))), nil
415426
}
416427

417428
r, err := json.Marshal(subIssues)
418429
if err != nil {
419430
return nil, fmt.Errorf("failed to marshal response: %w", err)
420431
}
421432

422-
return mcp.NewToolResultText(string(r)), nil
433+
return utils.NewToolResultText(string(r)), nil
423434
}
424435

425436
func GetIssueLabels(ctx context.Context, client *githubv4.Client, owner string, repo string, issueNumber int, _ FeatureFlags) (*mcp.CallToolResult, error) {
@@ -471,54 +482,60 @@ func GetIssueLabels(ctx context.Context, client *githubv4.Client, owner string,
471482
return nil, fmt.Errorf("failed to marshal response: %w", err)
472483
}
473484

474-
return mcp.NewToolResultText(string(out)), nil
485+
return utils.NewToolResultText(string(out)), nil
475486

476487
}
477488

478489
// ListIssueTypes creates a tool to list defined issue types for an organization. This can be used to understand supported issue type values for creating or updating issues.
479-
func ListIssueTypes(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
480-
481-
return mcp.NewTool("list_issue_types",
482-
mcp.WithDescription(t("TOOL_LIST_ISSUE_TYPES_FOR_ORG", "List supported issue types for repository owner (organization).")),
483-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
490+
func ListIssueTypes(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) {
491+
return mcp.Tool{
492+
Name: "list_issue_types",
493+
Description: t("TOOL_LIST_ISSUE_TYPES_FOR_ORG", "List supported issue types for repository owner (organization)."),
494+
Annotations: &mcp.ToolAnnotations{
484495
Title: t("TOOL_LIST_ISSUE_TYPES_USER_TITLE", "List available issue types"),
485-
ReadOnlyHint: ToBoolPtr(true),
486-
}),
487-
mcp.WithString("owner",
488-
mcp.Required(),
489-
mcp.Description("The organization owner of the repository"),
490-
),
491-
),
492-
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
493-
owner, err := RequiredParam[string](request, "owner")
496+
ReadOnlyHint: true,
497+
},
498+
InputSchema: &jsonschema.Schema{
499+
Type: "object",
500+
Properties: map[string]*jsonschema.Schema{
501+
"owner": {
502+
Type: "string",
503+
Description: "The organization owner of the repository",
504+
},
505+
},
506+
Required: []string{"owner"},
507+
},
508+
},
509+
func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) {
510+
owner, err := RequiredParam[string](args, "owner")
494511
if err != nil {
495-
return mcp.NewToolResultError(err.Error()), nil
512+
return utils.NewToolResultError(err.Error()), nil, nil
496513
}
497514

498515
client, err := getClient(ctx)
499516
if err != nil {
500-
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
517+
return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
501518
}
502519
issueTypes, resp, err := client.Organizations.ListIssueTypes(ctx, owner)
503520
if err != nil {
504-
return nil, fmt.Errorf("failed to list issue types: %w", err)
521+
return utils.NewToolResultErrorFromErr("failed to list issue types", err), nil, nil
505522
}
506523
defer func() { _ = resp.Body.Close() }()
507524

508525
if resp.StatusCode != http.StatusOK {
509526
body, err := io.ReadAll(resp.Body)
510527
if err != nil {
511-
return nil, fmt.Errorf("failed to read response body: %w", err)
528+
return utils.NewToolResultErrorFromErr("failed to read response body", err), nil, nil
512529
}
513-
return mcp.NewToolResultError(fmt.Sprintf("failed to list issue types: %s", string(body))), nil
530+
return utils.NewToolResultError(fmt.Sprintf("failed to list issue types: %s", string(body))), nil, nil
514531
}
515532

516533
r, err := json.Marshal(issueTypes)
517534
if err != nil {
518-
return nil, fmt.Errorf("failed to marshal issue types: %w", err)
535+
return utils.NewToolResultErrorFromErr("failed to marshal issue types", err), nil, nil
519536
}
520537

521-
return mcp.NewToolResultText(string(r)), nil
538+
return utils.NewToolResultText(string(r)), nil, nil
522539
}
523540
}
524541

0 commit comments

Comments
 (0)