1+ package main
2+
3+ import (
4+ "fmt"
5+ "sort"
6+ "strings"
7+
8+ "github.com/AlecAivazis/survey/v2"
9+ "github.com/spf13/cobra"
10+ )
11+
12+ var wizardCmd = & cobra.Command {
13+ Use : "wizard" ,
14+ Short : "Interactive configuration wizard for GitHub MCP Server" ,
15+ Long : `This wizard will help you configure which specific tools to enable.` ,
16+ RunE : runWizard ,
17+ }
18+
19+ type toolInfo struct {
20+ name string
21+ description string
22+ toolsetName string
23+ isReadOnly bool
24+ }
25+
26+ // Define all available tools statically to avoid runtime initialization issues
27+ var availableTools = map [string ][]toolInfo {
28+ "repos" : {
29+ {name : "search_repositories" , description : "Search for GitHub repositories" , isReadOnly : true },
30+ {name : "get_file_contents" , description : "Get file contents from a repository" , isReadOnly : true },
31+ {name : "list_commits" , description : "List commits in a repository" , isReadOnly : true },
32+ {name : "search_code" , description : "Search code across GitHub" , isReadOnly : true },
33+ {name : "get_commit" , description : "Get details of a specific commit" , isReadOnly : true },
34+ {name : "list_branches" , description : "List branches in a repository" , isReadOnly : true },
35+ {name : "list_tags" , description : "List tags in a repository" , isReadOnly : true },
36+ {name : "get_tag" , description : "Get details of a specific tag" , isReadOnly : true },
37+ {name : "list_releases" , description : "List releases in a repository" , isReadOnly : true },
38+ {name : "get_latest_release" , description : "Get the latest release of a repository" , isReadOnly : true },
39+ {name : "get_release_by_tag" , description : "Get a release by its tag" , isReadOnly : true },
40+ {name : "list_starred_repositories" , description : "List starred repositories" , isReadOnly : true },
41+ {name : "create_or_update_file" , description : "Create or update a file in a repository" , isReadOnly : false },
42+ {name : "create_repository" , description : "Create a new repository" , isReadOnly : false },
43+ {name : "fork_repository" , description : "Fork a repository" , isReadOnly : false },
44+ {name : "create_branch" , description : "Create a new branch" , isReadOnly : false },
45+ {name : "push_files" , description : "Push files to a repository" , isReadOnly : false },
46+ {name : "delete_file" , description : "Delete a file from a repository" , isReadOnly : false },
47+ {name : "star_repository" , description : "Star a repository" , isReadOnly : false },
48+ {name : "unstar_repository" , description : "Unstar a repository" , isReadOnly : false },
49+ },
50+ "issues" : {
51+ {name : "get_issue" , description : "Get details of an issue" , isReadOnly : true },
52+ {name : "search_issues" , description : "Search for issues" , isReadOnly : true },
53+ {name : "list_issues" , description : "List issues in a repository" , isReadOnly : true },
54+ {name : "get_issue_comments" , description : "Get comments on an issue" , isReadOnly : true },
55+ {name : "list_issue_types" , description : "List available issue types" , isReadOnly : true },
56+ {name : "list_sub_issues" , description : "List sub-issues of an issue" , isReadOnly : true },
57+ {name : "create_issue" , description : "Create a new issue" , isReadOnly : false },
58+ {name : "add_issue_comment" , description : "Add a comment to an issue" , isReadOnly : false },
59+ {name : "update_issue" , description : "Update an issue" , isReadOnly : false },
60+ {name : "assign_copilot_to_issue" , description : "Assign Copilot to an issue" , isReadOnly : false },
61+ {name : "add_sub_issue" , description : "Add a sub-issue" , isReadOnly : false },
62+ {name : "remove_sub_issue" , description : "Remove a sub-issue" , isReadOnly : false },
63+ {name : "reprioritize_sub_issue" , description : "Reprioritize a sub-issue" , isReadOnly : false },
64+ },
65+ "users" : {
66+ {name : "search_users" , description : "Search for GitHub users" , isReadOnly : true },
67+ },
68+ "orgs" : {
69+ {name : "search_orgs" , description : "Search for GitHub organizations" , isReadOnly : true },
70+ },
71+ "pull_requests" : {
72+ {name : "get_pull_request" , description : "Get details of a pull request" , isReadOnly : true },
73+ {name : "list_pull_requests" , description : "List pull requests in a repository" , isReadOnly : true },
74+ {name : "get_pull_request_files" , description : "Get files changed in a pull request" , isReadOnly : true },
75+ {name : "search_pull_requests" , description : "Search for pull requests" , isReadOnly : true },
76+ {name : "get_pull_request_status" , description : "Get status of a pull request" , isReadOnly : true },
77+ {name : "get_pull_request_review_comments" , description : "Get review comments on a pull request" , isReadOnly : true },
78+ {name : "get_pull_request_reviews" , description : "Get reviews on a pull request" , isReadOnly : true },
79+ {name : "get_pull_request_diff" , description : "Get diff of a pull request" , isReadOnly : true },
80+ {name : "merge_pull_request" , description : "Merge a pull request" , isReadOnly : false },
81+ {name : "update_pull_request_branch" , description : "Update pull request branch" , isReadOnly : false },
82+ {name : "create_pull_request" , description : "Create a pull request" , isReadOnly : false },
83+ {name : "update_pull_request" , description : "Update a pull request" , isReadOnly : false },
84+ {name : "request_copilot_review" , description : "Request Copilot review" , isReadOnly : false },
85+ {name : "create_and_submit_pull_request_review" , description : "Create and submit a PR review" , isReadOnly : false },
86+ {name : "create_pending_pull_request_review" , description : "Create a pending PR review" , isReadOnly : false },
87+ {name : "add_comment_to_pending_review" , description : "Add comment to pending review" , isReadOnly : false },
88+ {name : "submit_pending_pull_request_review" , description : "Submit a pending PR review" , isReadOnly : false },
89+ {name : "delete_pending_pull_request_review" , description : "Delete a pending PR review" , isReadOnly : false },
90+ },
91+ "code_security" : {
92+ {name : "get_code_scanning_alert" , description : "Get a code scanning alert" , isReadOnly : true },
93+ {name : "list_code_scanning_alerts" , description : "List code scanning alerts" , isReadOnly : true },
94+ },
95+ "secret_protection" : {
96+ {name : "get_secret_scanning_alert" , description : "Get a secret scanning alert" , isReadOnly : true },
97+ {name : "list_secret_scanning_alerts" , description : "List secret scanning alerts" , isReadOnly : true },
98+ },
99+ "dependabot" : {
100+ {name : "get_dependabot_alert" , description : "Get a Dependabot alert" , isReadOnly : true },
101+ {name : "list_dependabot_alerts" , description : "List Dependabot alerts" , isReadOnly : true },
102+ },
103+ "notifications" : {
104+ {name : "list_notifications" , description : "List notifications" , isReadOnly : true },
105+ {name : "get_notification_details" , description : "Get notification details" , isReadOnly : true },
106+ {name : "dismiss_notification" , description : "Dismiss a notification" , isReadOnly : false },
107+ {name : "mark_all_notifications_read" , description : "Mark all notifications as read" , isReadOnly : false },
108+ {name : "manage_notification_subscription" , description : "Manage notification subscription" , isReadOnly : false },
109+ {name : "manage_repository_notification_subscription" , description : "Manage repository notification subscription" , isReadOnly : false },
110+ },
111+ "discussions" : {
112+ {name : "list_discussions" , description : "List discussions" , isReadOnly : true },
113+ {name : "get_discussion" , description : "Get a discussion" , isReadOnly : true },
114+ {name : "get_discussion_comments" , description : "Get discussion comments" , isReadOnly : true },
115+ {name : "list_discussion_categories" , description : "List discussion categories" , isReadOnly : true },
116+ },
117+ "actions" : {
118+ {name : "list_workflows" , description : "List workflows" , isReadOnly : true },
119+ {name : "list_workflow_runs" , description : "List workflow runs" , isReadOnly : true },
120+ {name : "get_workflow_run" , description : "Get a workflow run" , isReadOnly : true },
121+ {name : "get_workflow_run_logs" , description : "Get workflow run logs" , isReadOnly : true },
122+ {name : "list_workflow_jobs" , description : "List workflow jobs" , isReadOnly : true },
123+ {name : "get_job_logs" , description : "Get job logs" , isReadOnly : true },
124+ {name : "list_workflow_run_artifacts" , description : "List workflow run artifacts" , isReadOnly : true },
125+ {name : "download_workflow_run_artifact" , description : "Download workflow run artifact" , isReadOnly : true },
126+ {name : "get_workflow_run_usage" , description : "Get workflow run usage" , isReadOnly : true },
127+ {name : "run_workflow" , description : "Run a workflow" , isReadOnly : false },
128+ {name : "rerun_workflow_run" , description : "Rerun a workflow run" , isReadOnly : false },
129+ {name : "rerun_failed_jobs" , description : "Rerun failed jobs" , isReadOnly : false },
130+ {name : "cancel_workflow_run" , description : "Cancel a workflow run" , isReadOnly : false },
131+ {name : "delete_workflow_run_logs" , description : "Delete workflow run logs" , isReadOnly : false },
132+ },
133+ "security_advisories" : {
134+ {name : "list_global_security_advisories" , description : "List global security advisories" , isReadOnly : true },
135+ {name : "get_global_security_advisory" , description : "Get a global security advisory" , isReadOnly : true },
136+ {name : "list_repository_security_advisories" , description : "List repository security advisories" , isReadOnly : true },
137+ {name : "list_org_repository_security_advisories" , description : "List org repository security advisories" , isReadOnly : true },
138+ },
139+ "context" : {
140+ {name : "get_me" , description : "Get current user information" , isReadOnly : true },
141+ {name : "get_teams" , description : "Get teams" , isReadOnly : true },
142+ {name : "get_team_members" , description : "Get team members" , isReadOnly : true },
143+ },
144+ "gists" : {
145+ {name : "list_gists" , description : "List gists" , isReadOnly : true },
146+ {name : "create_gist" , description : "Create a gist" , isReadOnly : false },
147+ {name : "update_gist" , description : "Update a gist" , isReadOnly : false },
148+ },
149+ "projects" : {
150+ {name : "list_projects" , description : "List projects" , isReadOnly : true },
151+ {name : "get_project" , description : "Get a project" , isReadOnly : true },
152+ {name : "list_project_fields" , description : "List project fields" , isReadOnly : true },
153+ {name : "get_project_field" , description : "Get a project field" , isReadOnly : true },
154+ {name : "list_project_items" , description : "List project items" , isReadOnly : true },
155+ {name : "get_project_item" , description : "Get a project item" , isReadOnly : true },
156+ {name : "add_project_item" , description : "Add a project item" , isReadOnly : false },
157+ {name : "delete_project_item" , description : "Delete a project item" , isReadOnly : false },
158+ {name : "update_project_item" , description : "Update a project item" , isReadOnly : false },
159+ },
160+ }
161+
162+ func runWizard (cmd * cobra.Command , args []string ) error {
163+ fmt .Println ("🧙 GitHub MCP Server Configuration Wizard" )
164+ fmt .Println ("==========================================" )
165+ fmt .Println ()
166+
167+ // Collect all available tools
168+ var allTools []toolInfo
169+ for toolsetName , tools := range availableTools {
170+ for _ , tool := range tools {
171+ tool .toolsetName = toolsetName
172+ allTools = append (allTools , tool )
173+ }
174+ }
175+
176+ // Sort tools alphabetically by name
177+ sort .Slice (allTools , func (i , j int ) bool {
178+ return allTools [i ].name < allTools [j ].name
179+ })
180+
181+ // Create options for the survey with formatted descriptions
182+ var toolOptions []string
183+ toolMap := make (map [string ]toolInfo )
184+ for _ , tool := range allTools {
185+ readOnlyTag := ""
186+ if tool .isReadOnly {
187+ readOnlyTag = " [READ-ONLY]"
188+ }
189+ option := fmt .Sprintf ("%s%s - %s (from %s)" , tool .name , readOnlyTag , tool .description , tool .toolsetName )
190+ toolOptions = append (toolOptions , option )
191+ toolMap [option ] = tool
192+ }
193+
194+ // Select individual tools
195+ selectedOptions := []string {}
196+ toolPrompt := & survey.MultiSelect {
197+ Message : "Select the specific tools you want to enable:" ,
198+ Options : toolOptions ,
199+ PageSize : 20 ,
200+ }
201+
202+ if err := survey .AskOne (toolPrompt , & selectedOptions ); err != nil {
203+ return err
204+ }
205+
206+ // Parse selected tools
207+ selectedTools := []string {}
208+ for _ , selection := range selectedOptions {
209+ if tool , exists := toolMap [selection ]; exists {
210+ selectedTools = append (selectedTools , tool .name )
211+ }
212+ }
213+
214+ // Build args array
215+ cmdArgs := []string {
216+ "run" ,
217+ "cmd/github-mcp-server/main.go" ,
218+ "cmd/github-mcp-server/wizard.go" ,
219+ "stdio" ,
220+ }
221+
222+ // Add specific tools
223+ if len (selectedTools ) > 0 {
224+ cmdArgs = append (cmdArgs , "--tools" )
225+ cmdArgs = append (cmdArgs , strings .Join (selectedTools , "," ))
226+ }
227+
228+ // Display the command
229+ fmt .Println ()
230+ fmt .Println ("✅ Configuration Complete!" )
231+ fmt .Println ("==========================" )
232+ fmt .Println ()
233+ fmt .Println ("Selected tools:" , strings .Join (selectedTools , ", " ))
234+ fmt .Println ()
235+ fmt .Println ("Add this to your mcp.json configuration:" )
236+ fmt .Println ()
237+
238+ // Print JSON format
239+ fmt .Println (`"args": [` )
240+ for i , arg := range cmdArgs {
241+ comma := ","
242+ if i == len (cmdArgs )- 1 {
243+ comma = ""
244+ }
245+ fmt .Printf (` "%s"%s` + "\n " , arg , comma )
246+ }
247+ fmt .Println (`],` )
248+
249+ fmt .Println ()
250+ fmt .Println ("Or run directly with:" )
251+ fmt .Printf ("go %s\n " , strings .Join (cmdArgs , " " ))
252+
253+ return nil
254+ }
0 commit comments