Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8d17eee

Browse files
move to new approach and update testing
1 parentde0a621 commit8d17eee

17 files changed

+408
-250
lines changed

‎internal/ghmcp/server.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313
"syscall"
1414

15+
"github.com/github/github-mcp-server/pkg/errors"
1516
"github.com/github/github-mcp-server/pkg/github"
1617
mcplog"github.com/github/github-mcp-server/pkg/log"
1718
"github.com/github/github-mcp-server/pkg/raw"
@@ -90,6 +91,13 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
9091

9192
hooks:=&server.Hooks{
9293
OnBeforeInitialize: []server.OnBeforeInitializeFunc{beforeInit},
94+
OnBeforeAny: []server.BeforeAnyHookFunc{
95+
func(ctx context.Context,_any,_ mcp.MCPMethod,_any) {
96+
// Ensure the context is cleared of any previous errors
97+
// as context isn't propagated through middleware
98+
errors.ContextWithGitHubErrors(ctx)
99+
},
100+
},
93101
}
94102

95103
ghServer:=github.NewServer(cfg.Version,server.WithHooks(hooks))
@@ -222,7 +230,8 @@ func RunStdioServer(cfg StdioServerConfig) error {
222230
loggedIO:=mcplog.NewIOLogger(in,out,logrusLogger)
223231
in,out=loggedIO,loggedIO
224232
}
225-
233+
// enable GitHub errors in the context
234+
ctx:=errors.ContextWithGitHubErrors(ctx)
226235
errC<-stdioServer.Listen(ctx,in,out)
227236
}()
228237

‎pkg/errors/error.go

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package errors
22

33
import (
4+
"context"
45
"fmt"
56

67
"github.com/google/go-github/v72/github"
8+
"github.com/mark3labs/mcp-go/mcp"
79
)
810

911
typeGitHubAPIErrorstruct {
@@ -12,7 +14,8 @@ type GitHubAPIError struct {
1214
Errerror`json:"-"`
1315
}
1416

15-
funcNewGitHubAPIError(messagestring,resp*github.Response,errerror)*GitHubAPIError {
17+
// NewGitHubAPIError creates a new GitHubAPIError with the provided message, response, and error.
18+
funcnewGitHubAPIError(messagestring,resp*github.Response,errerror)*GitHubAPIError {
1619
return&GitHubAPIError{
1720
Message:message,
1821
Response:resp,
@@ -29,7 +32,7 @@ type GitHubGraphQLError struct {
2932
Errerror`json:"-"`
3033
}
3134

32-
funcNewGitHubGraphQLError(messagestring,errerror)*GitHubGraphQLError {
35+
funcnewGitHubGraphQLError(messagestring,errerror)*GitHubGraphQLError {
3336
return&GitHubGraphQLError{
3437
Message:message,
3538
Err:err,
@@ -39,3 +42,84 @@ func NewGitHubGraphQLError(message string, err error) *GitHubGraphQLError {
3942
func (e*GitHubGraphQLError)Error()string {
4043
returnfmt.Errorf("%s: %w",e.Message,e.Err).Error()
4144
}
45+
46+
typeGitHubErrorKeystruct{}
47+
typeGitHubCtxErrorsstruct {
48+
api []*GitHubAPIError
49+
graphQL []*GitHubGraphQLError
50+
}
51+
52+
// ContextWithGitHubErrors updates or creates a context with a pointer to GitHub error information (to be used by middleware).
53+
funcContextWithGitHubErrors(ctx context.Context) context.Context {
54+
ifctx==nil {
55+
ctx=context.Background()
56+
}
57+
ifval,ok:=ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors);ok {
58+
// If the context already has GitHubCtxErrors, we just empty the slices to start fresh
59+
val.api= []*GitHubAPIError{}
60+
val.graphQL= []*GitHubGraphQLError{}
61+
}else {
62+
// If not, we create a new GitHubCtxErrors and set it in the context
63+
ctx=context.WithValue(ctx,GitHubErrorKey{},&GitHubCtxErrors{})
64+
}
65+
66+
returnctx
67+
}
68+
69+
// GetGitHubAPIErrors retrieves the slice of GitHubAPIErrors from the context.
70+
funcGetGitHubAPIErrors(ctx context.Context) ([]*GitHubAPIError,error) {
71+
ifval,ok:=ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors);ok {
72+
returnval.api,nil// return the slice of API errors from the context
73+
}
74+
returnnil,fmt.Errorf("context does not contain GitHubCtxErrors")
75+
}
76+
77+
// GetGitHubGraphQLErrors retrieves the slice of GitHubGraphQLErrors from the context.
78+
funcGetGitHubGraphQLErrors(ctx context.Context) ([]*GitHubGraphQLError,error) {
79+
ifval,ok:=ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors);ok {
80+
returnval.graphQL,nil// return the slice of GraphQL errors from the context
81+
}
82+
returnnil,fmt.Errorf("context does not contain GitHubCtxErrors")
83+
}
84+
85+
funcNewGitHubAPIErrorToCtx(ctx context.Context,messagestring,resp*github.Response,errerror) (context.Context,error) {
86+
apiErr:=newGitHubAPIError(message,resp,err)
87+
ifctx!=nil {
88+
addGitHubAPIErrorToContext(ctx,apiErr)
89+
}
90+
returnctx,nil
91+
}
92+
93+
funcaddGitHubAPIErrorToContext(ctx context.Context,err*GitHubAPIError) (context.Context,error) {
94+
ifval,ok:=ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors);ok {
95+
val.api=append(val.api,err)// append the error to the existing slice in the context
96+
returnctx,nil
97+
}
98+
returnnil,fmt.Errorf("context does not contain GitHubCtxErrors")
99+
}
100+
101+
funcaddGitHubGraphQLErrorToContext(ctx context.Context,err*GitHubGraphQLError) (context.Context,error) {
102+
ifval,ok:=ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors);ok {
103+
val.graphQL=append(val.graphQL,err)// append the error to the existing slice in the context
104+
returnctx,nil
105+
}
106+
returnnil,fmt.Errorf("context does not contain GitHubCtxErrors")
107+
}
108+
109+
// NewGitHubAPIErrorResponse returns an mcp.NewToolResultError and retains the error in the context for access via middleware
110+
funcNewGitHubAPIErrorResponse(ctx context.Context,messagestring,resp*github.Response,errerror)*mcp.CallToolResult {
111+
apiErr:=newGitHubAPIError(message,resp,err)
112+
ifctx!=nil {
113+
addGitHubAPIErrorToContext(ctx,apiErr)
114+
}
115+
returnmcp.NewToolResultErrorFromErr(message,err)
116+
}
117+
118+
// NewGitHubGraphQLErrorResponse returns an mcp.NewToolResultError and retains the error in the context for access via middleware
119+
funcNewGitHubGraphQLErrorResponse(ctx context.Context,messagestring,errerror)*mcp.CallToolResult {
120+
graphQLErr:=newGitHubGraphQLError(message,err)
121+
ifctx!=nil {
122+
addGitHubGraphQLErrorToContext(ctx,graphQLErr)
123+
}
124+
returnmcp.NewToolResultErrorFromErr(message,err)
125+
}

‎pkg/github/actions.go

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strconv"
1010
"strings"
1111

12+
ghErrors"github.com/github/github-mcp-server/pkg/errors"
1213
"github.com/github/github-mcp-server/pkg/translations"
1314
"github.com/google/go-github/v72/github"
1415
"github.com/mark3labs/mcp-go/mcp"
@@ -644,7 +645,7 @@ func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo
644645
Filter:"latest",
645646
})
646647
iferr!=nil {
647-
returnmcp.NewToolResultError(fmt.Sprintf("failed to list workflow jobs: %v",err)),nil
648+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to list workflow jobs",resp,err),nil
648649
}
649650
deferfunc() {_=resp.Body.Close() }()
650651

@@ -670,15 +671,18 @@ func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo
670671
// Collect logs for all failed jobs
671672
varlogResults []map[string]any
672673
for_,job:=rangefailedJobs {
673-
jobResult,err:=getJobLogData(ctx,client,owner,repo,job.GetID(),job.GetName(),returnContent)
674+
jobResult,resp,err:=getJobLogData(ctx,client,owner,repo,job.GetID(),job.GetName(),returnContent)
674675
iferr!=nil {
675676
// Continue with other jobs even if one fails
676677
jobResult=map[string]any{
677678
"job_id":job.GetID(),
678679
"job_name":job.GetName(),
679680
"error":err.Error(),
680681
}
682+
// Enable reporting of status codes and error causes
683+
ghErrors.NewGitHubAPIErrorToCtx(ctx,"failed to get job logs",resp,err)
681684
}
685+
682686
logResults=append(logResults,jobResult)
683687
}
684688

@@ -701,9 +705,9 @@ func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo
701705

702706
// handleSingleJobLogs gets logs for a single job
703707
funchandleSingleJobLogs(ctx context.Context,client*github.Client,owner,repostring,jobIDint64,returnContentbool) (*mcp.CallToolResult,error) {
704-
jobResult,err:=getJobLogData(ctx,client,owner,repo,jobID,"",returnContent)
708+
jobResult,resp,err:=getJobLogData(ctx,client,owner,repo,jobID,"",returnContent)
705709
iferr!=nil {
706-
returnmcp.NewToolResultError(err.Error()),nil
710+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to get job logs",resp,err),nil
707711
}
708712

709713
r,err:=json.Marshal(jobResult)
@@ -715,11 +719,11 @@ func handleSingleJobLogs(ctx context.Context, client *github.Client, owner, repo
715719
}
716720

717721
// getJobLogData retrieves log data for a single job, either as URL or content
718-
funcgetJobLogData(ctx context.Context,client*github.Client,owner,repostring,jobIDint64,jobNamestring,returnContentbool) (map[string]any,error) {
722+
funcgetJobLogData(ctx context.Context,client*github.Client,owner,repostring,jobIDint64,jobNamestring,returnContentbool) (map[string]any,*github.Response,error) {
719723
// Get the download URL for the job logs
720724
url,resp,err:=client.Actions.GetWorkflowJobLogs(ctx,owner,repo,jobID,1)
721725
iferr!=nil {
722-
returnnil,fmt.Errorf("failed to get job logs for job %d: %w",jobID,err)
726+
returnnil,resp,fmt.Errorf("failed to get job logs for job %d: %w",jobID,err)
723727
}
724728
deferfunc() {_=resp.Body.Close() }()
725729

@@ -732,9 +736,10 @@ func getJobLogData(ctx context.Context, client *github.Client, owner, repo strin
732736

733737
ifreturnContent {
734738
// Download and return the actual log content
735-
content,err:=downloadLogContent(url.String())
739+
// TODO we can use a generic http error or an interface instead of github.Response
740+
content,_,err:=downloadLogContent(url.String())
736741
iferr!=nil {
737-
returnnil,fmt.Errorf("failed to download log content for job %d: %w",jobID,err)
742+
returnnil,nil,fmt.Errorf("failed to download log content for job %d: %w",jobID,err)
738743
}
739744
result["logs_content"]=content
740745
result["message"]="Job logs content retrieved successfully"
@@ -745,29 +750,29 @@ func getJobLogData(ctx context.Context, client *github.Client, owner, repo strin
745750
result["note"]="The logs_url provides a download link for the individual job logs in plain text format. Use return_content=true to get the actual log content."
746751
}
747752

748-
returnresult,nil
753+
returnresult,resp,nil
749754
}
750755

751756
// downloadLogContent downloads the actual log content from a GitHub logs URL
752-
funcdownloadLogContent(logURLstring) (string,error) {
757+
funcdownloadLogContent(logURLstring) (string,*http.Response,error) {
753758
httpResp,err:=http.Get(logURL)//nolint:gosec // URLs are provided by GitHub API and are safe
754759
iferr!=nil {
755-
return"",fmt.Errorf("failed to download logs: %w",err)
760+
return"",httpResp,fmt.Errorf("failed to download logs: %w",err)
756761
}
757762
deferfunc() {_=httpResp.Body.Close() }()
758763

759764
ifhttpResp.StatusCode!=http.StatusOK {
760-
return"",fmt.Errorf("failed to download logs: HTTP %d",httpResp.StatusCode)
765+
return"",httpResp,fmt.Errorf("failed to download logs: HTTP %d",httpResp.StatusCode)
761766
}
762767

763768
content,err:=io.ReadAll(httpResp.Body)
764769
iferr!=nil {
765-
return"",fmt.Errorf("failed to read log content: %w",err)
770+
return"",httpResp,fmt.Errorf("failed to read log content: %w",err)
766771
}
767772

768773
// Clean up and format the log content for better readability
769774
logContent:=strings.TrimSpace(string(content))
770-
returnlogContent,nil
775+
returnlogContent,httpResp,nil
771776
}
772777

773778
// RerunWorkflowRun creates a tool to re-run an entire workflow run
@@ -813,7 +818,7 @@ func RerunWorkflowRun(getClient GetClientFn, t translations.TranslationHelperFun
813818

814819
resp,err:=client.Actions.RerunWorkflowByID(ctx,owner,repo,runID)
815820
iferr!=nil {
816-
returnnil,fmt.Errorf("failed to rerun workflow run: %w",err)
821+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to rerun workflow run",resp,err),nil
817822
}
818823
deferfunc() {_=resp.Body.Close() }()
819824

@@ -876,7 +881,7 @@ func RerunFailedJobs(getClient GetClientFn, t translations.TranslationHelperFunc
876881

877882
resp,err:=client.Actions.RerunFailedJobsByID(ctx,owner,repo,runID)
878883
iferr!=nil {
879-
returnnil,fmt.Errorf("failed to rerun failed jobs: %w",err)
884+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to rerun failed jobs",resp,err),nil
880885
}
881886
deferfunc() {_=resp.Body.Close() }()
882887

@@ -939,7 +944,7 @@ func CancelWorkflowRun(getClient GetClientFn, t translations.TranslationHelperFu
939944

940945
resp,err:=client.Actions.CancelWorkflowRunByID(ctx,owner,repo,runID)
941946
iferr!=nil {
942-
returnnil,fmt.Errorf("failed to cancel workflow run: %w",err)
947+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to cancel workflow run",resp,err),nil
943948
}
944949
deferfunc() {_=resp.Body.Close() }()
945950

@@ -1024,7 +1029,7 @@ func ListWorkflowRunArtifacts(getClient GetClientFn, t translations.TranslationH
10241029

10251030
artifacts,resp,err:=client.Actions.ListWorkflowRunArtifacts(ctx,owner,repo,runID,opts)
10261031
iferr!=nil {
1027-
returnnil,fmt.Errorf("failed to list workflow run artifacts: %w",err)
1032+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to list workflow run artifacts",resp,err),nil
10281033
}
10291034
deferfunc() {_=resp.Body.Close() }()
10301035

@@ -1081,7 +1086,7 @@ func DownloadWorkflowRunArtifact(getClient GetClientFn, t translations.Translati
10811086
// Get the download URL for the artifact
10821087
url,resp,err:=client.Actions.DownloadArtifact(ctx,owner,repo,artifactID,1)
10831088
iferr!=nil {
1084-
returnnil,fmt.Errorf("failed to get artifact download URL: %w",err)
1089+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to get artifact download URL",resp,err),nil
10851090
}
10861091
deferfunc() {_=resp.Body.Close() }()
10871092

@@ -1146,7 +1151,7 @@ func DeleteWorkflowRunLogs(getClient GetClientFn, t translations.TranslationHelp
11461151

11471152
resp,err:=client.Actions.DeleteWorkflowRunLogs(ctx,owner,repo,runID)
11481153
iferr!=nil {
1149-
returnnil,fmt.Errorf("failed to delete workflow run logs: %w",err)
1154+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to delete workflow run logs",resp,err),nil
11501155
}
11511156
deferfunc() {_=resp.Body.Close() }()
11521157

@@ -1209,7 +1214,7 @@ func GetWorkflowRunUsage(getClient GetClientFn, t translations.TranslationHelper
12091214

12101215
usage,resp,err:=client.Actions.GetWorkflowRunUsageByID(ctx,owner,repo,runID)
12111216
iferr!=nil {
1212-
returnnil,fmt.Errorf("failed to get workflow run usage: %w",err)
1217+
returnghErrors.NewGitHubAPIErrorResponse(ctx,"failed to get workflow run usage",resp,err),nil
12131218
}
12141219
deferfunc() {_=resp.Body.Close() }()
12151220

‎pkg/github/code_scanning.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ func GetCodeScanningAlert(getClient GetClientFn, t translations.TranslationHelpe
5555

5656
alert,resp,err:=client.CodeScanning.GetAlert(ctx,owner,repo,int64(alertNumber))
5757
iferr!=nil {
58-
returnnil,ghErrors.NewGitHubAPIError(
58+
returnghErrors.NewGitHubAPIErrorResponse(ctx,
5959
"failed to get alert",
6060
resp,
6161
err,
62-
)
62+
),nil
6363
}
6464
deferfunc() {_=resp.Body.Close() }()
6565

@@ -143,11 +143,11 @@ func ListCodeScanningAlerts(getClient GetClientFn, t translations.TranslationHel
143143
}
144144
alerts,resp,err:=client.CodeScanning.ListAlertsForRepo(ctx,owner,repo,&github.AlertListOptions{Ref:ref,State:state,Severity:severity,ToolName:toolName})
145145
iferr!=nil {
146-
returnnil,ghErrors.NewGitHubAPIError(
146+
returnghErrors.NewGitHubAPIErrorResponse(ctx,
147147
"failed to list alerts",
148148
resp,
149149
err,
150-
)
150+
),nil
151151
}
152152
deferfunc() {_=resp.Body.Close() }()
153153

‎pkg/github/code_scanning_test.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,15 @@ func Test_GetCodeScanningAlert(t *testing.T) {
9494

9595
// Verify results
9696
iftc.expectError {
97-
require.Error(t,err)
98-
assert.Contains(t,err.Error(),tc.expectedErrMsg)
97+
require.NoError(t,err)
98+
require.True(t,result.IsError)
99+
errorContent:=getErrorResult(t,result)
100+
assert.Contains(t,errorContent.Text,tc.expectedErrMsg)
99101
return
100102
}
101103

102104
require.NoError(t,err)
105+
require.False(t,result.IsError)
103106

104107
// Parse the result and get the text content if no error
105108
textContent:=getTextResult(t,result)
@@ -217,12 +220,15 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
217220

218221
// Verify results
219222
iftc.expectError {
220-
require.Error(t,err)
221-
assert.Contains(t,err.Error(),tc.expectedErrMsg)
223+
require.NoError(t,err)
224+
require.True(t,result.IsError)
225+
errorContent:=getErrorResult(t,result)
226+
assert.Contains(t,errorContent.Text,tc.expectedErrMsg)
222227
return
223228
}
224229

225230
require.NoError(t,err)
231+
require.False(t,result.IsError)
226232

227233
// Parse the result and get the text content if no error
228234
textContent:=getTextResult(t,result)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp