/ operation / operation.go
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  }