mcp.go 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package main
  2. import (
  3. "os"
  4. "os/signal"
  5. "syscall"
  6. "time"
  7. "github.com/spf13/cobra"
  8. "gogs.netdome.biz/paul/cheapo/internal/mcpserver"
  9. )
  10. // version is the value reported to MCP clients; overridable via -ldflags.
  11. var version = "dev"
  12. func mcpCmd() *cobra.Command {
  13. var (
  14. httpAddr string
  15. httpPath string
  16. token string
  17. stateless bool
  18. sessionTimeout time.Duration
  19. )
  20. cmd := &cobra.Command{
  21. Use: "mcp",
  22. Short: "MCP server (HTTP or stdio) for agents and MCP clients",
  23. Long: `Exposes the free games data as an MCP server.
  24. Tools:
  25. list_offers Mirrors "cheapo list" — mode: free (default), upcoming,
  26. deals; plus store, country, locale
  27. The server is stateless: every call returns the full current state;
  28. deduplication ("what is new?") is the client's job.
  29. Without --http the server runs over stdio (for clients that start the
  30. binary themselves). With --http it runs as an HTTP service (Streamable
  31. HTTP) — the mode for remote clients, multiple users and monitoring.
  32. Examples:
  33. cheapo mcp --http 127.0.0.1:8080
  34. cheapo mcp --http 0.0.0.0:8080 --token "$CHEAPO_MCP_TOKEN"
  35. cheapo mcp # stdio
  36. Region/locale/stores come from the global flags (--country, --locale,
  37. --store) and apply as defaults for the tool calls.`,
  38. Args: cobra.NoArgs,
  39. RunE: func(cmd *cobra.Command, _ []string) error {
  40. ctx, stop := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM)
  41. defer stop()
  42. // CHEAPO_MCP_TOKEN may also come from a .env file (godotenv);
  43. // real environment variables take precedence.
  44. loadEnv()
  45. srv := mcpserver.New(mcpserver.Options{
  46. Store: flagStore,
  47. Country: flagCountry,
  48. Locale: flagLocale,
  49. Version: version,
  50. })
  51. if httpAddr == "" {
  52. return srv.Run(ctx)
  53. }
  54. if token == "" {
  55. token = os.Getenv("CHEAPO_MCP_TOKEN")
  56. }
  57. return srv.RunHTTP(ctx, httpAddr, mcpserver.HTTPOptions{
  58. Path: httpPath,
  59. Token: token,
  60. Stateless: stateless,
  61. SessionTimeout: sessionTimeout,
  62. })
  63. },
  64. }
  65. cmd.Flags().StringVar(&httpAddr, "http", "", "HTTP address (e.g. 127.0.0.1:8080); empty = stdio")
  66. cmd.Flags().StringVar(&httpPath, "path", "/mcp", "HTTP path of the MCP endpoint")
  67. cmd.Flags().StringVar(&token, "token", "", "Bearer token for HTTP (default: $CHEAPO_MCP_TOKEN)")
  68. cmd.Flags().BoolVar(&stateless, "stateless", false, "HTTP without sessions (for proxies/load balancers)")
  69. cmd.Flags().DurationVar(&sessionTimeout, "session-timeout", 30*time.Minute, "Idle timeout for HTTP sessions")
  70. return cmd
  71. }