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)). +
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))
}