operation.go
1 package operation 2 3 import ( 4 "fmt" 5 6 "codeberg.org/goern/forgejo-mcp/v2/operation/actions" 7 "codeberg.org/goern/forgejo-mcp/v2/operation/issue" 8 "codeberg.org/goern/forgejo-mcp/v2/operation/org" 9 "codeberg.org/goern/forgejo-mcp/v2/operation/pull" 10 "codeberg.org/goern/forgejo-mcp/v2/operation/repo" 11 "codeberg.org/goern/forgejo-mcp/v2/operation/search" 12 "codeberg.org/goern/forgejo-mcp/v2/operation/user" 13 "codeberg.org/goern/forgejo-mcp/v2/operation/version" 14 "codeberg.org/goern/forgejo-mcp/v2/pkg/flag" 15 "codeberg.org/goern/forgejo-mcp/v2/pkg/forgejo" 16 "codeberg.org/goern/forgejo-mcp/v2/pkg/log" 17 18 "github.com/mark3labs/mcp-go/server" 19 ) 20 21 var ( 22 mcpServer *server.MCPServer 23 ) 24 25 func RegisterTool(s *server.MCPServer) { 26 log.Info("Registering MCP tools") 27 28 RegisterUserTool(s) 29 RegisterRepoTool(s) 30 RegisterIssueTool(s) 31 RegisterPullTool(s) 32 RegisterPullReviewTool(s) 33 RegisterSearchTool(s) 34 RegisterVersionTool(s) 35 RegisterActionsTool(s) 36 RegisterOrgTool(s) 37 38 log.Info("All MCP tools registered successfully") 39 } 40 41 // Per-domain registration functions exposed for CLI domain grouping. 42 43 func RegisterUserTool(s *server.MCPServer) { 44 user.RegisterTool(s) 45 log.Debug("Registered user tools") 46 } 47 48 func RegisterRepoTool(s *server.MCPServer) { 49 repo.RegisterTool(s) 50 log.Debug("Registered repository tools") 51 } 52 53 func RegisterIssueTool(s *server.MCPServer) { 54 issue.RegisterTool(s) 55 log.Debug("Registered issue tools") 56 } 57 58 func RegisterPullTool(s *server.MCPServer) { 59 pull.RegisterTool(s) 60 log.Debug("Registered pull request tools") 61 } 62 63 func RegisterPullReviewTool(s *server.MCPServer) { 64 pull.RegisterReviewTools(s) 65 log.Debug("Registered pull review write tools") 66 } 67 68 func RegisterSearchTool(s *server.MCPServer) { 69 search.RegisterTool(s) 70 log.Debug("Registered search tools") 71 } 72 73 func RegisterVersionTool(s *server.MCPServer) { 74 version.RegisterTool(s) 75 log.Debug("Registered version tools") 76 } 77 78 func RegisterActionsTool(s *server.MCPServer) { 79 actions.RegisterTool(s) 80 log.Debug("Registered actions tools") 81 } 82 83 func RegisterOrgTool(s *server.MCPServer) { 84 org.RegisterTool(s) 85 log.Debug("Registered org tools") 86 } 87 88 func Run(transport, version string) error { 89 flag.Version = version 90 mcpServer = newMCPServer(version) 91 RegisterTool(mcpServer) 92 93 // Test connection to Forgejo instance before starting the server 94 log.Info("Testing connection to Forgejo instance", 95 log.SanitizedURLField("url", flag.URL), 96 ) 97 if err := testConnection(); err != nil { 98 log.Error("Failed to connect to Forgejo instance", 99 log.SanitizedURLField("url", flag.URL), 100 log.ErrorField(err), 101 ) 102 return fmt.Errorf("connection test failed: %w", err) 103 } 104 log.Info("Successfully connected to Forgejo instance", 105 log.SanitizedURLField("url", flag.URL), 106 ) 107 108 switch transport { 109 case "stdio": 110 log.Info("Starting MCP server with stdio transport") 111 log.Info("MCP server ready for stdio communication") 112 if err := server.ServeStdio(mcpServer); err != nil { 113 log.Error("MCP stdio server failed", 114 log.ErrorField(err), 115 ) 116 return err 117 } 118 log.Info("MCP stdio server shutdown") 119 case "sse": 120 sseServer := server.NewSSEServer(mcpServer) 121 log.Info("Starting MCP SSE server", 122 log.IntField("port", flag.SSEPort), 123 ) 124 log.Info("MCP SSE server ready for connections", 125 log.IntField("port", flag.SSEPort), 126 log.StringField("endpoint", fmt.Sprintf("http://localhost:%d", flag.SSEPort)), 127 ) 128 if err := sseServer.Start(fmt.Sprintf(":%d", flag.SSEPort)); err != nil { 129 log.Error("Failed to start SSE server", 130 log.IntField("port", flag.SSEPort), 131 log.ErrorField(err), 132 ) 133 return fmt.Errorf("failed to start SSE server: %w", err) 134 } 135 log.Info("MCP SSE server shutdown") 136 case "http": 137 httpServer := server.NewStreamableHTTPServer(mcpServer) 138 log.Info("Starting MCP streamable HTTP server", 139 log.IntField("port", flag.HTTPPort), 140 ) 141 log.Info("MCP streamable HTTP server ready for connections", 142 log.IntField("port", flag.HTTPPort), 143 log.StringField("endpoint", fmt.Sprintf("http://localhost:%d", flag.HTTPPort)), 144 ) 145 if err := httpServer.Start(fmt.Sprintf(":%d", flag.HTTPPort)); err != nil { 146 log.Error("Failed to start streamable HTTP server", 147 log.IntField("port", flag.HTTPPort), 148 log.ErrorField(err), 149 ) 150 return fmt.Errorf("failed to start streamable HTTP server: %w", err) 151 } 152 log.Info("MCP streamable HTTP server shutdown") 153 default: 154 log.Error("Invalid transport configuration", 155 log.StringField("transport", transport), 156 log.StringField("valid_options", "stdio, sse, http"), 157 ) 158 return fmt.Errorf("invalid transport type: %s. Must be 'stdio', 'sse', or 'http'", transport) 159 } 160 return nil 161 } 162 163 func testConnection() error { 164 return forgejo.VerifyConnection() 165 } 166 167 168 func newMCPServer(version string) *server.MCPServer { 169 return server.NewMCPServer( 170 "Forgejo MCP Server", 171 version, 172 server.WithLogging(), 173 ) 174 }