Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.5
# sync with script/lint.sh
version: v2.9
7 changes: 7 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ linters:
- gosec
- makezero
- misspell
- modernize
- nakedret
- revive
- errcheck
- staticcheck
- govet
- ineffassign
- intrange
- unused
exclusions:
generated: lax
Expand All @@ -27,6 +29,11 @@ linters:
- third_party$
- builtin$
- examples$
- internal/githubv4mock
rules:
- linters:
- revive
text: "var-naming: avoid package names that conflict with Go standard library package names"
settings:
staticcheck:
checks:
Expand Down
19 changes: 6 additions & 13 deletions cmd/github-mcp-server/generate_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/url"
"os"
"slices"
"sort"
"strings"

Expand Down Expand Up @@ -234,7 +235,7 @@ func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) {

for i, propName := range paramNames {
prop := schema.Properties[propName]
required := contains(schema.Required, propName)
required := slices.Contains(schema.Required, propName)
requiredStr := "optional"
if required {
requiredStr = "required"
Expand Down Expand Up @@ -289,15 +290,6 @@ func scopesEqual(a, b []string) bool {
return true
}

func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}

// indentMultilineDescription adds the specified indent to all lines after the first line.
// This ensures that multi-line descriptions maintain proper markdown list formatting.
func indentMultilineDescription(description, indent string) string {
Expand All @@ -319,14 +311,14 @@ func replaceSection(content, startMarker, endMarker, newContent string) (string,
start := fmt.Sprintf("<!-- %s -->", startMarker)
end := fmt.Sprintf("<!-- %s -->", endMarker)

startIdx := strings.Index(content, start)
before, _, ok := strings.Cut(content, start)
endIdx := strings.Index(content, end)
if startIdx == -1 || endIdx == -1 {
if !ok || endIdx == -1 {
return "", fmt.Errorf("markers not found: %s / %s", start, end)
}

var buf strings.Builder
buf.WriteString(content[:startIdx])
buf.WriteString(before)
buf.WriteString(start)
buf.WriteString("\n")
buf.WriteString(newContent)
Expand Down Expand Up @@ -426,6 +418,7 @@ func generateRemoteOnlyToolsetsDoc() string {

return strings.TrimSuffix(buf.String(), "\n")
}

func generateDeprecatedAliasesDocs(docsPath string) error {
// Read the current file
content, err := os.ReadFile(docsPath) //#nosec G304
Expand Down
16 changes: 8 additions & 8 deletions cmd/mcpcurl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ type (

// RequestParams contains the tool name and arguments
RequestParams struct {
Name string `json:"name"`
Arguments map[string]interface{} `json:"arguments"`
Name string `json:"name"`
Arguments map[string]any `json:"arguments"`
}

// Content matches the response format of a text content response
Expand Down Expand Up @@ -308,8 +308,8 @@ func addCommandFromTool(toolsCmd *cobra.Command, tool *Tool, prettyPrint bool) {
}

// buildArgumentsMap extracts flag values into a map of arguments
func buildArgumentsMap(cmd *cobra.Command, tool *Tool) (map[string]interface{}, error) {
arguments := make(map[string]interface{})
func buildArgumentsMap(cmd *cobra.Command, tool *Tool) (map[string]any, error) {
arguments := make(map[string]any)

for name, prop := range tool.InputSchema.Properties {
switch prop.Type {
Expand Down Expand Up @@ -340,7 +340,7 @@ func buildArgumentsMap(cmd *cobra.Command, tool *Tool) (map[string]interface{},
}
case "object":
if jsonStr, _ := cmd.Flags().GetString(name + "-json"); jsonStr != "" {
var jsonArray []interface{}
var jsonArray []any
if err := json.Unmarshal([]byte(jsonStr), &jsonArray); err != nil {
return nil, fmt.Errorf("error parsing JSON for %s: %w", name, err)
}
Expand All @@ -355,7 +355,7 @@ func buildArgumentsMap(cmd *cobra.Command, tool *Tool) (map[string]interface{},
}

// buildJSONRPCRequest creates a JSON-RPC request with the given tool name and arguments
func buildJSONRPCRequest(method, toolName string, arguments map[string]interface{}) (string, error) {
func buildJSONRPCRequest(method, toolName string, arguments map[string]any) (string, error) {
id, err := rand.Int(rand.Reader, big.NewInt(10000))
if err != nil {
return "", fmt.Errorf("failed to generate random ID: %w", err)
Expand Down Expand Up @@ -432,7 +432,7 @@ func printResponse(response string, prettyPrint bool) error {
// Extract text from content items of type "text"
for _, content := range resp.Result.Content {
if content.Type == "text" {
var textContentObj map[string]interface{}
var textContentObj map[string]any
err := json.Unmarshal([]byte(content.Text), &textContentObj)

if err == nil {
Expand All @@ -445,7 +445,7 @@ func printResponse(response string, prettyPrint bool) error {
}

// Fallback parsing as JSONL
var textContentList []map[string]interface{}
var textContentList []map[string]any
if err := json.Unmarshal([]byte(content.Text), &textContentList); err != nil {
return fmt.Errorf("failed to parse text content as a list: %w", err)
}
Expand Down
20 changes: 10 additions & 10 deletions internal/toolsnaps/toolsnaps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,23 +195,23 @@ func TestToolSnapKeysSorted(t *testing.T) {

// Given a tool with fields that could be in any order
type complexTool struct {
Name string `json:"name"`
Description string `json:"description"`
Properties map[string]interface{} `json:"properties"`
Annotations map[string]interface{} `json:"annotations"`
Name string `json:"name"`
Description string `json:"description"`
Properties map[string]any `json:"properties"`
Annotations map[string]any `json:"annotations"`
}

tool := complexTool{
Name: "test_tool",
Description: "A test tool",
Properties: map[string]interface{}{
Properties: map[string]any{
"zzz": "last",
"aaa": "first",
"mmm": "middle",
"owner": map[string]interface{}{"type": "string", "description": "Owner"},
"repo": map[string]interface{}{"type": "string", "description": "Repo"},
"owner": map[string]any{"type": "string", "description": "Owner"},
"repo": map[string]any{"type": "string", "description": "Repo"},
},
Annotations: map[string]interface{}{
Annotations: map[string]any{
"readOnly": true,
"title": "Test",
},
Expand All @@ -227,7 +227,7 @@ func TestToolSnapKeysSorted(t *testing.T) {
require.NoError(t, err)

// Verify that the JSON is properly sorted by checking key order
var parsed map[string]interface{}
var parsed map[string]any
err = json.Unmarshal(snapJSON, &parsed)
require.NoError(t, err)

Expand Down Expand Up @@ -285,7 +285,7 @@ func TestStructFieldOrderingSortedAlphabetically(t *testing.T) {
aFieldIndex := -1
mFieldIndex := -1
zFieldIndex := -1
for i := 0; i < len(snapStr)-7; i++ {
for i := range len(snapStr) - 7 {
switch snapStr[i : i+6] {
case "aField":
aFieldIndex = i
Expand Down
7 changes: 2 additions & 5 deletions pkg/buffer/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,14 @@ func ProcessResponseAsRingBufferToEnd(httpResp *http.Response, maxJobLogLines in
}

var result []string
linesInBuffer := totalLines
if linesInBuffer > maxJobLogLines {
linesInBuffer = maxJobLogLines
}
linesInBuffer := min(totalLines, maxJobLogLines)

startIndex := 0
if totalLines > maxJobLogLines {
startIndex = writeIndex
}

for i := 0; i < linesInBuffer; i++ {
for i := range linesInBuffer {
idx := (startIndex + i) % maxJobLogLines
if validLines[idx] {
result = append(result, lines[idx])
Expand Down
11 changes: 4 additions & 7 deletions pkg/github/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,7 @@ func downloadLogContent(ctx context.Context, logURL string, tailLines int, maxLi
return "", 0, httpResp, fmt.Errorf("failed to download logs: HTTP %d", httpResp.StatusCode)
}

bufferSize := tailLines
if bufferSize > maxLines {
bufferSize = maxLines
}
bufferSize := min(tailLines, maxLines)

processedInput, totalLines, httpResp, err := buffer.ProcessResponseAsRingBufferToEnd(httpResp, bufferSize)
if err != nil {
Expand Down Expand Up @@ -577,9 +574,9 @@ func ActionsRunTrigger(t translations.TranslationHelperFunc) inventory.ServerToo
runID, _ := OptionalIntParam(args, "run_id")

// Get optional inputs parameter
var inputs map[string]interface{}
var inputs map[string]any
if requestInputs, ok := args["inputs"]; ok {
if inputsMap, ok := requestInputs.(map[string]interface{}); ok {
if inputsMap, ok := requestInputs.(map[string]any); ok {
inputs = inputsMap
}
}
Expand Down Expand Up @@ -982,7 +979,7 @@ func getWorkflowRunUsage(ctx context.Context, client *github.Client, owner, repo
return utils.NewToolResultText(string(r)), nil, nil
}

func runWorkflow(ctx context.Context, client *github.Client, owner, repo, workflowID, ref string, inputs map[string]interface{}) (*mcp.CallToolResult, any, error) {
func runWorkflow(ctx context.Context, client *github.Client, owner, repo, workflowID, ref string, inputs map[string]any) (*mcp.CallToolResult, any, error) {
event := github.CreateWorkflowDispatchEventRequest{
Ref: ref,
Inputs: inputs,
Expand Down
12 changes: 6 additions & 6 deletions pkg/github/code_scanning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
tests := []struct {
name string
mockedClient *http.Client
requestArgs map[string]interface{}
requestArgs map[string]any
expectError bool
expectedAlert *github.Alert
expectedErrMsg string
Expand All @@ -51,7 +51,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
GetReposCodeScanningAlertsByOwnerByRepoByAlertNumber: mockResponse(t, http.StatusOK, mockAlert),
}),
requestArgs: map[string]interface{}{
requestArgs: map[string]any{
"owner": "owner",
"repo": "repo",
"alertNumber": float64(42),
Expand All @@ -67,7 +67,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
_, _ = w.Write([]byte(`{"message": "Not Found"}`))
}),
}),
requestArgs: map[string]interface{}{
requestArgs: map[string]any{
"owner": "owner",
"repo": "repo",
"alertNumber": float64(9999),
Expand Down Expand Up @@ -158,7 +158,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
tests := []struct {
name string
mockedClient *http.Client
requestArgs map[string]interface{}
requestArgs map[string]any
expectError bool
expectedAlerts []*github.Alert
expectedErrMsg string
Expand All @@ -175,7 +175,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
mockResponse(t, http.StatusOK, mockAlerts),
),
}),
requestArgs: map[string]interface{}{
requestArgs: map[string]any{
"owner": "owner",
"repo": "repo",
"ref": "main",
Expand All @@ -194,7 +194,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
_, _ = w.Write([]byte(`{"message": "Unauthorized access"}`))
}),
}),
requestArgs: map[string]interface{}{
requestArgs: map[string]any{
"owner": "owner",
"repo": "repo",
},
Expand Down
4 changes: 2 additions & 2 deletions pkg/github/context_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func GetTeams(t translations.TranslationHelperFunc) inventory.ServerTool {
} `graphql:"organizations(first: 100)"`
} `graphql:"user(login: $login)"`
}
vars := map[string]interface{}{
vars := map[string]any{
"login": githubv4.String(username),
}
if err := gqlClient.Query(ctx, &q, vars); err != nil {
Expand Down Expand Up @@ -262,7 +262,7 @@ func GetTeamMembers(t translations.TranslationHelperFunc) inventory.ServerTool {
} `graphql:"team(slug: $teamSlug)"`
} `graphql:"organization(login: $org)"`
}
vars := map[string]interface{}{
vars := map[string]any{
"org": githubv4.String(org),
"teamSlug": githubv4.String(teamSlug),
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/github/context_tools_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func Test_GetTeams(t *testing.T) {
// to ensure each test gets a fresh client
gqlClientForTestuser := func() *githubv4.Client {
queryStr := "query($login:String!){user(login: $login){organizations(first: 100){nodes{login,teams(first: 100, userLogins: [$login]){nodes{name,slug,description}}}}}}"
vars := map[string]interface{}{
vars := map[string]any{
"login": "testuser",
}
matcher := githubv4mock.NewQueryMatcher(queryStr, vars, mockTeamsResponse)
Expand All @@ -225,7 +225,7 @@ func Test_GetTeams(t *testing.T) {

gqlClientForSpecificuser := func() *githubv4.Client {
queryStr := "query($login:String!){user(login: $login){organizations(first: 100){nodes{login,teams(first: 100, userLogins: [$login]){nodes{name,slug,description}}}}}}"
vars := map[string]interface{}{
vars := map[string]any{
"login": "specificuser",
}
matcher := githubv4mock.NewQueryMatcher(queryStr, vars, mockTeamsResponse)
Expand All @@ -235,7 +235,7 @@ func Test_GetTeams(t *testing.T) {

gqlClientNoTeams := func() *githubv4.Client {
queryStr := "query($login:String!){user(login: $login){organizations(first: 100){nodes{login,teams(first: 100, userLogins: [$login]){nodes{name,slug,description}}}}}}"
vars := map[string]interface{}{
vars := map[string]any{
"login": "testuser",
}
matcher := githubv4mock.NewQueryMatcher(queryStr, vars, mockNoTeamsResponse)
Expand Down Expand Up @@ -419,7 +419,7 @@ func Test_GetTeamMembers(t *testing.T) {
// Create GQL clients for different test scenarios
gqlClientWithMembers := func() *githubv4.Client {
queryStr := "query($org:String!$teamSlug:String!){organization(login: $org){team(slug: $teamSlug){members(first: 100){nodes{login}}}}}"
vars := map[string]interface{}{
vars := map[string]any{
"org": "testorg",
"teamSlug": "testteam",
}
Expand All @@ -430,7 +430,7 @@ func Test_GetTeamMembers(t *testing.T) {

gqlClientNoMembers := func() *githubv4.Client {
queryStr := "query($org:String!$teamSlug:String!){organization(login: $org){team(slug: $teamSlug){members(first: 100){nodes{login}}}}}"
vars := map[string]interface{}{
vars := map[string]any{
"org": "testorg",
"teamSlug": "emptyteam",
}
Expand Down
Loading
Loading