Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Dockerfile.gateway
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM golang:1.22-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o gateway cmd/gateway/main.go

FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/gateway .
CMD ["./gateway"]
14 changes: 14 additions & 0 deletions Dockerfile.orchestrator
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM golang:1.22-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o orchestrator cmd/orchestrator/main.go

FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/orchestrator .
CMD ["./orchestrator"]
14 changes: 14 additions & 0 deletions Dockerfile.worker
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM golang:1.22-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o worker cmd/worker/main.go

FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/worker .
CMD ["./worker"]
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.PHONY: up down migrate

up:
docker-compose up -d

down:
docker-compose down

migrate:
# This is a placeholder since we are using docker-entrypoint-initdb.d for now
# In a real scenario, we would use a migration tool like golang-migrate
@echo "Migrations are applied automatically on first run via docker-entrypoint-initdb.d"
46 changes: 46 additions & 0 deletions cmd/gateway/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"log"
"net/http"
"os"

"github.com/gin-gonic/gin"
"github.com/mcp/mobile-worker/internal/gateway/jsonrpc"
"github.com/mcp/mobile-worker/internal/gateway/rest"
"github.com/mcp/mobile-worker/internal/gateway/websocket"
)

func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}

router := gin.Default()

// Initialize components
wsHub := websocket.NewHub()
go wsHub.Run()

rpcHandler := jsonrpc.NewHandler(wsHub)
restHandler := rest.NewHandler()

// Routes
router.POST("/jsonrpc", rpcHandler.Handle)
router.POST("/sessions", restHandler.CreateSession)
router.GET("/capabilities", restHandler.GetCapabilities)
router.GET("/ws/plan-events", func(c *gin.Context) {
websocket.ServeWs(wsHub, c.Writer, c.Request)
})

// Health check
router.GET("/healthz", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "healthy"})
})

log.Printf("Gateway starting on port %s", port)
if err := router.Run(":" + port); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}
46 changes: 46 additions & 0 deletions cmd/orchestrator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"log"
"net"
"os"

"google.golang.org/grpc"

"github.com/mcp/mobile-worker/internal/orchestrator"
pb "github.com/mcp/mobile-worker/pkg/proto/orchestrator"
"github.com/mcp/mobile-worker/internal/storage/redis"
)

func main() {
port := os.Getenv("PORT")
if port == "" {
port = "50051"
}

redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}

redisClient, err := redis.NewClient(redisAddr, "", 0)
if err != nil {
log.Fatalf("Failed to connect to Redis: %v", err)
}
defer redisClient.Close()

lis, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

s := grpc.NewServer()

orchService := orchestrator.NewService(redisClient)
pb.RegisterOrchestratorServer(s, orchService)

log.Printf("Orchestrator server listening on %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
34 changes: 34 additions & 0 deletions cmd/worker/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"log"
"net"
"os"

"google.golang.org/grpc"

"github.com/mcp/mobile-worker/internal/worker"
pb "github.com/mcp/mobile-worker/pkg/proto/worker"
)

func main() {
port := os.Getenv("PORT")
if port == "" {
port = "50052"
}

lis, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

s := grpc.NewServer()

workerService := worker.NewService()
pb.RegisterWorkerServer(s, workerService)

log.Printf("Worker server listening on %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
56 changes: 56 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mcp
ports:
- "5432:5432"
volumes:
- ./internal/storage/postgres/migrations:/docker-entrypoint-initdb.d

redis:
image: redis:7-alpine
ports:
- "6379:6379"

gateway:
build:
context: .
dockerfile: Dockerfile.gateway
ports:
- "8080:8080"
environment:
- REDIS_ADDR=redis:6379
- PORT=8080
depends_on:
- redis
- postgres
- orchestrator

orchestrator:
build:
context: .
dockerfile: Dockerfile.orchestrator
ports:
- "50051:50051"
environment:
- REDIS_ADDR=redis:6379
- PORT=50051
depends_on:
- redis
- postgres

worker:
build:
context: .
dockerfile: Dockerfile.worker
ports:
- "50052:50052"
environment:
- REDIS_ADDR=redis:6379
- PORT=50052
depends_on:
- redis
- orchestrator
53 changes: 53 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module github.com/mcp/mobile-worker

go 1.24.3

require (
github.com/bytedance/sonic v1.14.0 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/gin-gonic/gin v1.11.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.6 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
github.com/redis/go-redis/v9 v9.17.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/arch v0.20.0 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/text v0.30.0 // indirect
golang.org/x/tools v0.37.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
google.golang.org/grpc v1.77.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
)
Loading