|
5 | 5 | "encoding/json" |
6 | 6 | "errors" |
7 | 7 | "fmt" |
| 8 | + "net/http" |
8 | 9 | "os" |
9 | 10 | "os/signal" |
10 | 11 | "path/filepath" |
@@ -143,7 +144,7 @@ func main() { |
143 | 144 | } |
144 | 145 | return |
145 | 146 | case "mcp": |
146 | | - if err := runMCP(ctx, svc); err != nil { |
| 147 | + if err := runMCP(ctx, svc, os.Args[2:]); err != nil { |
147 | 148 | fmt.Fprintln(os.Stderr, "kairo mcp:", err) |
148 | 149 | os.Exit(2) |
149 | 150 | } |
@@ -360,6 +361,7 @@ func runHelp(args []string) { |
360 | 361 | fmt.Println(" completion Generate shell completion scripts") |
361 | 362 | fmt.Println(" export Export tasks to JSON or Markdown") |
362 | 363 | fmt.Println(" import Import tasks from JSON or Markdown") |
| 364 | + fmt.Println(" mcp Run the built-in MCP server") |
363 | 365 | fmt.Println(" sync Sync tasks with Git repository") |
364 | 366 | fmt.Println(" update Update Kairo to the latest version") |
365 | 367 | fmt.Println(" version Show the current version") |
@@ -394,12 +396,54 @@ func runHelp(args []string) { |
394 | 396 | fmt.Println("Sync tasks with Git repository.") |
395 | 397 | fmt.Println("\nUsage:") |
396 | 398 | fmt.Println(" kairo sync") |
| 399 | + case "mcp": |
| 400 | + fmt.Println("Run the built-in Model Context Protocol (MCP) server.") |
| 401 | + fmt.Println("\nUsage:") |
| 402 | + fmt.Println(" kairo mcp [port]") |
| 403 | + fmt.Println("\nExample:") |
| 404 | + fmt.Println(" kairo mcp (Runs in Stdio mode for local AI agents)") |
| 405 | + fmt.Println(" kairo mcp 8080 (Runs in SSE/HTTP mode on port 8080)") |
| 406 | + fmt.Println("\nDescription:") |
| 407 | + fmt.Println(" Exposes your Kairo tasks and tools to AI agents using the MCP standard.") |
| 408 | + fmt.Println(" Stdio mode is used for local integration (e.g. Claude Desktop).") |
| 409 | + fmt.Println(" SSE mode allows remote connections via HTTP.") |
397 | 410 | default: |
398 | 411 | fmt.Printf("Unknown help topic %q\n", args[0]) |
399 | 412 | } |
400 | 413 | } |
401 | | -func runMCP(ctx context.Context, svc service.TaskService) error { |
| 414 | +func runMCP(ctx context.Context, svc service.TaskService, args []string) error { |
402 | 415 | s := mcp.NewServer(svc) |
| 416 | + |
| 417 | + cfg, _ := config.Load() |
| 418 | + port := os.Getenv("KAIRO_MCP_PORT") |
| 419 | + if port == "" { |
| 420 | + port = cfg.App.MCPPort |
| 421 | + } |
| 422 | + if len(args) > 0 { |
| 423 | + port = args[0] |
| 424 | + } |
| 425 | + |
| 426 | + if port != "" { |
| 427 | + addr := ":" + port |
| 428 | + baseURL := "http://localhost" + addr |
| 429 | + sseServer := mcpserver.NewSSEServer(s, mcpserver.WithBaseURL(baseURL)) |
| 430 | + |
| 431 | + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 432 | + w.Header().Set("Access-Control-Allow-Origin", "*") |
| 433 | + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") |
| 434 | + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") |
| 435 | + if r.Method == "OPTIONS" { |
| 436 | + w.WriteHeader(http.StatusOK) |
| 437 | + return |
| 438 | + } |
| 439 | + sseServer.ServeHTTP(w, r) |
| 440 | + }) |
| 441 | + |
| 442 | + fmt.Printf("Starting Kairo MCP SSE server on %s (CORS enabled)\n", addr) |
| 443 | + fmt.Printf("SSE endpoint: %s/sse\n", baseURL) |
| 444 | + return http.ListenAndServe(addr, handler) |
| 445 | + } |
| 446 | + |
403 | 447 | server := mcpserver.NewStdioServer(s) |
404 | 448 | return server.Listen(ctx, os.Stdin, os.Stdout) |
405 | 449 | } |
0 commit comments