Skip to content
Merged
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
26 changes: 26 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ services:
working_dir: /app
ports:
- '3001:3001'
# Reach a Google Workspace MCP sidecar running on the host (uvx, port 8000)
# as http://host.docker.internal:8000 (paired with MCP_INTERNAL_ALLOWLIST).
extra_hosts:
- 'host.docker.internal:host-gateway'
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/data
Expand Down Expand Up @@ -81,6 +85,12 @@ services:
SKILLS_CUSTOM_HOST_DIR: ${CLAWIX_HOST_SKILLS_DIR:-${PWD}/skills/custom}
# Public memory lives under the persistent /data bind mount so it
# survives `docker compose down` and image rebuilds.
# ---- MCP OAuth (set when using authType=oauth MCP servers) ----
MCP_OAUTH_CALLBACK_URL: http://localhost:3001/mcp/oauth/callback
WEB_BASE_URL: http://localhost:3000
# Google Workspace MCP sidecar runs on the HOST (uvx, port 8000), reached
# from this container via host.docker.internal (see extra_hosts below).
MCP_INTERNAL_ALLOWLIST: host.docker.internal:8000
command: >
sh -c "apt-get update && apt-get install -y --no-install-recommends docker.io && rm -rf /var/lib/apt/lists/* &&
corepack enable &&
Expand Down Expand Up @@ -199,6 +209,22 @@ services:
- clawix-browser-egress
- clawix-browser-net

# ---- Optional: Google Workspace MCP sidecar (OAuth bearer-proxy mode) ----
# Uncomment this service and set MCP_OAUTH_CALLBACK_URL + MCP_INTERNAL_ALLOWLIST
# on api-server above, then import the server in Admin → MCP with authType=oauth.
# clawix-gworkspace:
# image: ghcr.io/taylorwilsdon/google_workspace_mcp:latest
# environment:
# MCP_ENABLE_OAUTH21: 'true'
# EXTERNAL_OAUTH21_PROVIDER: 'true'
# WORKSPACE_MCP_STATELESS_MODE: 'true'
# GOOGLE_OAUTH_CLIENT_ID: '<your-google-oauth-client-id>'
# FASTMCP_SERVER_AUTH_GOOGLE_JWT_SIGNING_KEY: '<any-random-secret>'
# WORKSPACE_MCP_PORT: '8000'
# command: ['--transport', 'streamable-http', '--tools', 'gmail', 'drive', 'docs', 'sheets']
# networks:
# - clawix-internal

volumes:
postgres_data:
redis_data:
Expand Down
31 changes: 31 additions & 0 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ services:
# WhatsApp Baileys auth state — written under the persistent /data mount
# so the QR pairing survives container restarts.
WHATSAPP_AUTH_DIR: /data/whatsapp-auth
# ---- MCP per-user OAuth (authType=oauth servers) ----
# MCP_OAUTH_CALLBACK_URL: the OAuth redirect_uri — your PUBLIC API URL +
# /mcp/oauth/callback (must be https in prod and registered verbatim on
# the provider's OAuth client), e.g. https://api.example.com/mcp/oauth/callback
# WEB_BASE_URL: dashboard base used for the post-callback redirect.
# MCP_INTERNAL_ALLOWLIST: comma-separated host:port of internal MCP
# sidecars to allow past the SSRF guard, e.g. clawix-gworkspace:8000
MCP_OAUTH_CALLBACK_URL: ${MCP_OAUTH_CALLBACK_URL:-}
WEB_BASE_URL: ${WEB_BASE_URL:-http://localhost:3000}
MCP_INTERNAL_ALLOWLIST: ${MCP_INTERNAL_ALLOWLIST:-}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/data
Expand Down Expand Up @@ -188,6 +198,27 @@ services:
retries: 3
start_period: 30s

# ---- Google Workspace MCP sidecar (OAuth authType=oauth) — optional ----
# Self-hosted taylorwilsdon/google_workspace_mcp in bearer-proxy mode: clawix
# does the per-user Google OAuth and forwards each user's access token; this
# server validates it via Google userinfo and acts as that user. Uncomment,
# then set MCP_INTERNAL_ALLOWLIST=clawix-gworkspace:8000 + MCP_OAUTH_CALLBACK_URL
# on the api service, and import http://clawix-gworkspace:8000/mcp/ as authType=oauth.
# clawix-gworkspace:
# image: ghcr.io/taylorwilsdon/google_workspace_mcp:latest
# container_name: clawix-gworkspace
# restart: unless-stopped
# environment:
# MCP_ENABLE_OAUTH21: 'true'
# EXTERNAL_OAUTH21_PROVIDER: 'true' # clawix issues/forwards the Google token
# WORKSPACE_MCP_STATELESS_MODE: 'true' # no per-user session on the sidecar
# GOOGLE_OAUTH_CLIENT_ID: ${GOOGLE_OAUTH_CLIENT_ID:?same client id clawix uses}
# FASTMCP_SERVER_AUTH_GOOGLE_JWT_SIGNING_KEY: ${GWORKSPACE_JWT_SIGNING_KEY:?random secret}
# WORKSPACE_MCP_PORT: '8000'
# command: ['--transport', 'streamable-http', '--tools', 'gmail', 'drive', 'docs', 'sheets']
# networks:
# - clawix-internal

volumes:
postgres_data:
redis_data:
Expand Down
1 change: 1 addition & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@fastify/helmet": "^13.0.2",
"@fastify/multipart": "^10.0.0",
"@google/genai": "^1.50.1",
"@modelcontextprotocol/sdk": "^1.29.0",
"@mozilla/readability": "^0.6.0",
"@nestjs/common": "^11.0.0",
"@nestjs/config": "^4.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
-- CreateEnum
CREATE TYPE "McpTransport" AS ENUM ('http', 'sse');

-- CreateEnum
CREATE TYPE "McpAuthType" AS ENUM ('none', 'header', 'oauth');

-- CreateEnum
CREATE TYPE "McpServerStatus" AS ENUM ('active', 'error');

-- CreateEnum
CREATE TYPE "McpConnectionStatus" AS ENUM ('active', 'disabled', 'error', 'reauth_required');

-- AlterEnum
ALTER TYPE "NotificationType" ADD VALUE 'MCP_SERVER_ATTENTION';

-- AlterTable
ALTER TABLE "Policy" ADD COLUMN "allowMcp" BOOLEAN NOT NULL DEFAULT false;

-- CreateTable
CREATE TABLE "McpServer" (
"id" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"name" TEXT NOT NULL,
"enabled" BOOLEAN NOT NULL DEFAULT true,
"transportType" "McpTransport" NOT NULL DEFAULT 'http',
"url" TEXT NOT NULL,
"authType" "McpAuthType" NOT NULL DEFAULT 'none',
"authHeaderName" TEXT,
"credentialFormat" TEXT,
"setupInstructionsMd" TEXT NOT NULL DEFAULT '',
"discoveryCredentialEnc" TEXT,
"status" "McpServerStatus" NOT NULL DEFAULT 'active',
"lastError" TEXT,
"lastDiscoveredAt" TIMESTAMP(3),
"createdByUserId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "McpServer_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "McpTool" (
"id" TEXT NOT NULL,
"mcpServerId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT NOT NULL,
"inputSchema" JSONB NOT NULL,
"scanFlagged" BOOLEAN NOT NULL DEFAULT false,
"scanReason" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "McpTool_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "McpConnection" (
"id" TEXT NOT NULL,
"mcpServerId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"credentialEnc" TEXT,
"status" "McpConnectionStatus" NOT NULL DEFAULT 'active',
"lastError" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "McpConnection_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "McpServer_slug_key" ON "McpServer"("slug");

-- CreateIndex
CREATE UNIQUE INDEX "McpTool_mcpServerId_name_key" ON "McpTool"("mcpServerId", "name");

-- CreateIndex
CREATE INDEX "McpConnection_userId_idx" ON "McpConnection"("userId");

-- CreateIndex
CREATE UNIQUE INDEX "McpConnection_mcpServerId_userId_key" ON "McpConnection"("mcpServerId", "userId");

-- AddForeignKey
ALTER TABLE "McpTool" ADD CONSTRAINT "McpTool_mcpServerId_fkey" FOREIGN KEY ("mcpServerId") REFERENCES "McpServer"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "McpConnection" ADD CONSTRAINT "McpConnection_mcpServerId_fkey" FOREIGN KEY ("mcpServerId") REFERENCES "McpServer"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "McpConnection" ADD CONSTRAINT "McpConnection_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- DropForeignKey
ALTER TABLE "McpTool" DROP CONSTRAINT "McpTool_mcpServerId_fkey";

-- DropIndex
DROP INDEX "McpTool_mcpServerId_name_key";

-- AlterTable
ALTER TABLE "McpConnection" ADD COLUMN "lastDiscoveredAt" TIMESTAMP(3);

-- AlterTable
ALTER TABLE "McpServer" DROP COLUMN "discoveryCredentialEnc",
DROP COLUMN "lastDiscoveredAt",
DROP COLUMN "lastError",
DROP COLUMN "status";

-- AlterTable
ALTER TABLE "McpTool" DROP COLUMN "mcpServerId",
ADD COLUMN "mcpConnectionId" TEXT NOT NULL;

-- DropEnum
DROP TYPE "McpServerStatus";

-- CreateIndex
CREATE UNIQUE INDEX "McpTool_mcpConnectionId_name_key" ON "McpTool"("mcpConnectionId", "name");

-- AddForeignKey
ALTER TABLE "McpTool" ADD CONSTRAINT "McpTool_mcpConnectionId_fkey" FOREIGN KEY ("mcpConnectionId") REFERENCES "McpConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE "McpToolSuggestion" (
"id" TEXT NOT NULL,
"agentDefinitionId" TEXT NOT NULL,
"mcpServerId" TEXT NOT NULL,
"tiers" JSONB NOT NULL,
"model" TEXT NOT NULL,
"generatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "McpToolSuggestion_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "McpToolSuggestion_agentDefinitionId_mcpServerId_key" ON "McpToolSuggestion"("agentDefinitionId", "mcpServerId");

-- AddForeignKey
ALTER TABLE "McpToolSuggestion" ADD CONSTRAINT "McpToolSuggestion_agentDefinitionId_fkey" FOREIGN KEY ("agentDefinitionId") REFERENCES "AgentDefinition"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "McpToolSuggestion" ADD CONSTRAINT "McpToolSuggestion_mcpServerId_fkey" FOREIGN KEY ("mcpServerId") REFERENCES "McpServer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
Warnings:

- You are about to drop the `McpToolSuggestion` table. If the table is not empty, all the data it contains will be lost.

*/
-- DropForeignKey
ALTER TABLE "McpToolSuggestion" DROP CONSTRAINT "McpToolSuggestion_agentDefinitionId_fkey";

-- DropForeignKey
ALTER TABLE "McpToolSuggestion" DROP CONSTRAINT "McpToolSuggestion_mcpServerId_fkey";

-- AlterTable
ALTER TABLE "McpConnection" ADD COLUMN "tiers" JSONB;

-- DropTable
DROP TABLE "McpToolSuggestion";
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- AlterTable
ALTER TABLE "McpServer" ADD COLUMN "oauthAuthorizeUrl" TEXT,
ADD COLUMN "oauthClientId" TEXT,
ADD COLUMN "oauthClientSecretEnc" TEXT,
ADD COLUMN "oauthScopes" TEXT,
ADD COLUMN "oauthTokenUrl" TEXT;

-- CreateTable
CREATE TABLE "McpOAuthToken" (
"mcpConnectionId" TEXT NOT NULL,
"accessTokenEnc" TEXT NOT NULL,
"refreshTokenEnc" TEXT,
"expiresAt" TIMESTAMP(3) NOT NULL,
"scope" TEXT NOT NULL,
"lastRefreshedAt" TIMESTAMP(3),

CONSTRAINT "McpOAuthToken_pkey" PRIMARY KEY ("mcpConnectionId")
);

-- AddForeignKey
ALTER TABLE "McpOAuthToken" ADD CONSTRAINT "McpOAuthToken_mcpConnectionId_fkey" FOREIGN KEY ("mcpConnectionId") REFERENCES "McpConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- AlterTable
ALTER TABLE "McpServer" ADD COLUMN "oauthAutoDiscover" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "oauthDiscoveredAt" TIMESTAMP(3),
ADD COLUMN "oauthResource" TEXT;
Loading
Loading