- Add SSE (Server-Sent Events) transport mode support - Add Streamable HTTP transport mode support - Support single-user and multi-user authentication - Refactor server creation into shared function - Update documentation with MCP transport examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
113 lines
2.8 KiB
Go
113 lines
2.8 KiB
Go
/*
|
|
Copyright © 2025 Ronmi Ren <ronmi.ren@gmail.com
|
|
*/
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
"github.com/raohwork/forgejo-mcp/tools"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// httpCmd represents the http command
|
|
var httpCmd = &cobra.Command{
|
|
Use: "http",
|
|
Short: "Run MCP server in SSE mode",
|
|
Long: `Run the Forgejo MCP server with an HTTP interface.
|
|
|
|
This command starts a web server that allows MCP clients to connect over HTTP.
|
|
It supports both Server-Sent Events (SSE) on the /sse endpoint and a standard
|
|
request/response model on the / endpoint.
|
|
|
|
The server can operate in two modes:
|
|
- Single-user mode: When a --token is provided at startup, all operations
|
|
are performed using this single token. This is suitable for personal use
|
|
or dedicated services where one identity is sufficient.
|
|
- Multi-user mode: If no --token is provided, the server requires clients
|
|
to authenticate by providing their own token in the 'Authorization'
|
|
header of each request. This allows the server to act as a gateway for
|
|
multiple users.
|
|
|
|
This HTTP mode is ideal for:
|
|
- Web-based clients and services.
|
|
- Remote access to the Forgejo instance through MCP.
|
|
- Environments where a central MCP gateway is needed.
|
|
|
|
Example:
|
|
forgejo-mcp http --address :8080 --server https://git.example.com --token your_token`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
base := viper.GetString("server")
|
|
token := viper.GetString("token")
|
|
addr := viper.GetString("address")
|
|
if addr == "" {
|
|
addr = ":8080"
|
|
}
|
|
|
|
if base == "" {
|
|
cmd.Help()
|
|
os.Exit(1)
|
|
}
|
|
singleMode := token != ""
|
|
|
|
var cl *tools.Client
|
|
if singleMode {
|
|
c, err := tools.NewClient(base, token, "", nil)
|
|
if err != nil {
|
|
fmt.Printf("Error creating SDK client: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
cl = c
|
|
} else {
|
|
cl, _ = tools.NewClient(base, "", "9", nil)
|
|
}
|
|
|
|
getServer := func(q *http.Request) *mcp.Server {
|
|
if singleMode {
|
|
return createServer(cl)
|
|
}
|
|
|
|
mycl := cl
|
|
myToken := q.Header.Get("Authorization")
|
|
if myToken != "" {
|
|
if strings.HasPrefix(myToken, "Bearer ") {
|
|
myToken = myToken[7:]
|
|
}
|
|
c, err := tools.NewClient(base, myToken, "", nil)
|
|
if err == nil {
|
|
mycl = c
|
|
}
|
|
}
|
|
|
|
return createServer(mycl)
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/sse", mcp.NewSSEHandler(getServer))
|
|
mux.Handle("/", mcp.NewStreamableHTTPHandler(getServer, nil))
|
|
|
|
mode := "single"
|
|
if !singleMode {
|
|
mode = "multiuser"
|
|
}
|
|
fmt.Printf("Starting %s mode MCP server on %s\n", mode, addr)
|
|
err := http.ListenAndServe(addr, mux)
|
|
if err != nil {
|
|
fmt.Printf("Server exited with error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(httpCmd)
|
|
|
|
f := httpCmd.Flags()
|
|
f.String("address", ":8080", "Address to listen on for incoming connections")
|
|
viper.BindPFlags(f)
|
|
}
|