-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
129 lines (116 loc) · 3.52 KB
/
main.go
File metadata and controls
129 lines (116 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/modelcontextprotocol/go-sdk/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func run() error {
server := mcp.NewServer(&mcp.Implementation{
Name: "demo-github-mcp",
Title: "A demo github mcp server",
Version: "0.0.1",
}, nil)
mcp.AddTool(server, &mcp.Tool{
Name: "list-repositories",
Description: "A tool to list all repositories in a Github org",
InputSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"name": {
Type: "string",
Description: "GitHub organization name (e.g., kubernetes)",
},
"url": {
Type: "string",
Description: "GitHub organization URL (e.g., https://github.com/kubernetes)",
},
},
},
}, ListRepositories)
t := mcp.NewLoggingTransport(mcp.NewStdioTransport(), os.Stderr)
log.Println("🚀 MCP server starting up...")
if err := server.Run(context.Background(), t); err != nil {
log.Printf("Server failed: %v", err)
}
log.Println("🚀 MCP server shutting down...")
return nil
}
// User can pass in either the name of the org (example: kubernetes), or its URL (example: https://github.com/kubernetes)
type GithubOrgArgs struct {
Name string
URL string
}
func ListRepositories(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[GithubOrgArgs]) (*mcp.CallToolResultFor[struct{}], error) {
if params == nil {
return nil, fmt.Errorf("empty params")
}
args := params.Arguments
if args.Name == "" && args.URL == "" {
return nil, fmt.Errorf("empty args")
}
var apiURL string
var organization string
if args.URL != "" {
// If URL is provided, extract org name and build API URL
url := strings.TrimPrefix(args.URL, "https://")
url = strings.TrimPrefix(url, "http://")
url = strings.TrimPrefix(url, "github.com/")
url = strings.TrimSuffix(url, "/")
orgName := strings.Split(url, "/")[0]
apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", orgName)
organization = orgName
} else {
// Use the provided organization name
apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", args.Name)
organization = args.Name
}
// apiURL = fmt.Sprintf("%s%s", apiURL, "?per_page=100")
apiURL = apiURL + "?per_page=100"
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/vnd.github.v3+json")
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("GitHub API error (status %d): %s", resp.StatusCode, string(body))
}
type repository struct {
Name string `json:"name"`
FullName string `json:"full_name"`
HTMLURL string `json:"html_url"`
Private bool `json:"private"`
}
// Parse the JSON response
var repositories []repository
if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}
var result strings.Builder
result.WriteString(fmt.Sprintf("Repositories for organization %s:", organization))
for _, repo := range repositories {
result.WriteString(fmt.Sprintf("Name: %s, URL: %s", repo.Name, repo.HTMLURL))
}
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: result.String()},
},
}, nil
}
func main() {
log.Fatal(run())
}