diff --git a/.env.example b/.env.example index 3b4506d6f5..552ee7b511 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,7 @@ GOCLAW_GATEWAY_TOKEN= GOCLAW_ENCRYPTION_KEY= POSTGRES_PASSWORD= +MCP_RUNTIME_ACCESS_TOKEN= # --- Database (only for non-Docker deployments) --- # Docker Compose auto-builds this from POSTGRES_USER/PASSWORD/DB. diff --git a/README.md b/README.md index b1bc9ce694..feced0ee9d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ +> [!NOTE] +> This repository is a customized fork of GoClaw maintained by **trwng-thdat**, serving as the execution runtime container for the **AI Claw** SaaS platform ([ai-claw](file:///C:/HCMUS/Jarvis/ai-claw)). +

GoClaw

-

Multi-Tenant AI Agent Platform

+

Multi-Tenant AI Agent Platform (Custom Fork)

Multi-agent AI gateway built in Go. 20+ LLM providers. 7 channels. Multi-tenant PostgreSQL.
@@ -149,13 +152,13 @@ git tag lite-v0.1.0 && git push origin lite-v0.1.0 ### From Source ```bash -git clone -b main https://github.com/nextlevelbuilder/goclaw.git && cd goclaw +git clone -b dev https://github.com/trwng-thdat/goclaw.git && cd goclaw make build ./goclaw onboard # Interactive setup wizard source .env.local && ./goclaw ``` -> **Note:** The default branch is `dev` (active development). Use `-b main` to clone the stable release branch. +> **Note:** The default branch is `dev` (active development). We use the `dev` branch to build and align with the `ai-claw` parent repository. ### With Docker diff --git a/docker-compose.yml b/docker-compose.yml index dcaddb0ac8..0c8d5a4ed2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,7 @@ services: dockerfile: Dockerfile args: ENABLE_OTEL: "${ENABLE_OTEL:-false}" - ENABLE_EMBEDUI: "${ENABLE_EMBEDUI:-true}" # set to false when using WITH_WEB_NGINX + ENABLE_EMBEDUI: "${ENABLE_EMBEDUI:-true}" # set to false when using WITH_WEB_NGINX ENABLE_PYTHON: "${ENABLE_PYTHON:-true}" ENABLE_FULL_SKILLS: "${ENABLE_FULL_SKILLS:-false}" VERSION: "${GOCLAW_VERSION:-dev}" @@ -47,6 +47,7 @@ services: - GOCLAW_GATEWAY_TOKEN=${GOCLAW_GATEWAY_TOKEN:?run ./prepare-env.sh or set GOCLAW_GATEWAY_TOKEN} - GOCLAW_ENCRYPTION_KEY=${GOCLAW_ENCRYPTION_KEY:-} - GOCLAW_SKILLS_DIR=/app/data/skills + - GOCLAW_POSTGRES_DSN=postgres://postgres:postgres@postgres:5432/goclaw?sslmode=disable # Debug - GOCLAW_TRACE_VERBOSE=${GOCLAW_TRACE_VERBOSE:-0} - BITRIX24_LOG_RAW_EVENT=${BITRIX24_LOG_RAW_EVENT:-0} @@ -78,7 +79,14 @@ services: - default - goclaw-net restart: unless-stopped - + postgres: + image: pgvector/pgvector:pg16 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: goclaw + ports: + - "5434:5432" volumes: goclaw-data: goclaw-workspace: diff --git a/goclaw.exe b/goclaw.exe new file mode 100644 index 0000000000..899a603fd9 Binary files /dev/null and b/goclaw.exe differ diff --git a/internal/mcp/auth.go b/internal/mcp/auth.go new file mode 100644 index 0000000000..06793f01d2 --- /dev/null +++ b/internal/mcp/auth.go @@ -0,0 +1,38 @@ +package mcp + +import ( + "net/http" + "os" + + "github.com/google/uuid" + "github.com/nextlevelbuilder/goclaw/internal/store" +) + +// ContextAwareRoundTripper reads MCP_RUNTIME_ACCESS_TOKEN and attaches it to the header. +type ContextAwareRoundTripper struct { + Proxied http.RoundTripper +} + +func (c *ContextAwareRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + token := os.Getenv("MCP_RUNTIME_ACCESS_TOKEN") + if token != "" { + req.Header.Set("Authorization", "Bearer "+token) + } + + agentID := store.AgentIDFromContext(req.Context()) + userID := store.UserIDFromContext(req.Context()) + + if agentID != uuid.Nil { + req.Header.Set("X-GoClaw-Agent-Id", agentID.String()) + } + if userID != "" { + req.Header.Set("X-GoClaw-User-Id", userID) + } + + transport := c.Proxied + if transport == nil { + transport = http.DefaultTransport + } + return transport.RoundTrip(req) +} + diff --git a/internal/mcp/manager_connect.go b/internal/mcp/manager_connect.go index db10bbb5ba..e524ff6473 100644 --- a/internal/mcp/manager_connect.go +++ b/internal/mcp/manager_connect.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log/slog" + "net/http" "strings" "time" @@ -284,6 +285,13 @@ func (m *Manager) registerPoolBridgeTools(entry *poolEntry, serverName, toolPref // createClient creates the appropriate MCP client based on transport type. func createClient(transportType, command string, args []string, env map[string]string, url string, headers map[string]string) (*mcpclient.Client, error) { + + authHTTPClient := &http.Client{ + Transport: &ContextAwareRoundTripper{ + Proxied: http.DefaultTransport, + }, + } + switch transportType { case "stdio": envSlice := mapToEnvSlice(env) @@ -291,6 +299,8 @@ func createClient(transportType, command string, args []string, env map[string]s case "sse": var opts []transport.ClientOption + opts = append(opts, transport.WithHTTPClient(authHTTPClient)) + if len(headers) > 0 { opts = append(opts, mcpclient.WithHeaders(headers)) } @@ -298,6 +308,8 @@ func createClient(transportType, command string, args []string, env map[string]s case "streamable-http": var opts []transport.StreamableHTTPCOption + opts = append(opts, transport.WithHTTPBasicClient(authHTTPClient)) + if len(headers) > 0 { opts = append(opts, transport.WithHTTPHeaders(headers)) }