@@ -109,7 +109,7 @@ WebsiteProfiling/
│ ├── integrations/ # Google Search Console, GA4, Bing, CrUX
│ ├── llm/ # AI enrich + chat agent
│ ├── tools/ # Exports, audit query tools, MCP helpers
-│ ├── mcp/ # MCP server (340 read-only tools, domain bundles)
+│ ├── mcp/ # MCP server (stdio + remote HTTP, domain bundles)
│ ├── db/ # PostgreSQL storage layer
│ ├── commands/ # CLI subcommands
│ ├── cli.py # Pipeline entrypoint
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 6306d0e..0ba7a67 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -55,6 +55,27 @@ services:
profiles:
- worker
+ mcp:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ depends_on:
+ postgres:
+ condition: service_healthy
+ command: ['python', '-m', 'website_profiling.mcp.http']
+ environment:
+ WEBSITE_PROFILING_ROOT: /app
+ DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-website_profiling}
+ WP_MCP_HTTP_HOST: 0.0.0.0
+ WP_MCP_HTTP_PORT: 8000
+ WP_MCP_TOKEN: ${WP_MCP_TOKEN:?set WP_MCP_TOKEN}
+ WP_MCP_ALLOWED_HOSTS: ${WP_MCP_ALLOWED_HOSTS:-}
+ WP_MCP_ALLOWED_ORIGINS: ${WP_MCP_ALLOWED_ORIGINS:-}
+ WP_MCP_DOMAIN: ${WP_MCP_DOMAIN:-core}
+ WP_PROPERTY_ID: ${WP_PROPERTY_ID:-}
+ ports:
+ - '${MCP_PORT:-8000}:8000'
+
volumes:
pg-data:
profiling-data:
diff --git a/docker-compose.yml b/docker-compose.yml
index 94d375d..b4ecaa4 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -41,6 +41,27 @@ services:
retries: 3
start_period: 15s
+ # Optional remote MCP (Streamable HTTP). Uncomment and set WP_MCP_TOKEN / WP_MCP_ALLOWED_HOSTS.
+ # mcp:
+ # build:
+ # context: .
+ # dockerfile: Dockerfile
+ # image: website-profiling:latest
+ # depends_on:
+ # postgres:
+ # condition: service_healthy
+ # command: ['python', '-m', 'website_profiling.mcp.http']
+ # environment:
+ # WEBSITE_PROFILING_ROOT: /app
+ # DATABASE_URL: postgres://profiling:profiling@postgres:5432/website_profiling
+ # WP_MCP_HTTP_HOST: 0.0.0.0
+ # WP_MCP_HTTP_PORT: 8000
+ # WP_MCP_TOKEN: ${WP_MCP_TOKEN:-dev-mcp-token}
+ # WP_MCP_ALLOWED_HOSTS: localhost,127.0.0.1
+ # WP_MCP_DOMAIN: core
+ # ports:
+ # - "8000:8000"
+
volumes:
pg-data:
profiling-data:
diff --git a/docs/MCP.md b/docs/MCP.md
index 420bef6..5a5c0db 100644
--- a/docs/MCP.md
+++ b/docs/MCP.md
@@ -13,6 +13,7 @@ The same tool catalog powers in-app **AI Chat** at `/chat`.
- [Prerequisites](#prerequisites)
- [Domain-scoped servers](#domain-scoped-servers)
- [Configuration](#configuration)
+- [Remote Streamable HTTP](#remote-streamable-http)
- [MCP resources](#mcp-resources)
- [Tool reference](#tool-reference)
- [In-app chat](#in-app-chat)
@@ -31,12 +32,14 @@ export DATABASE_URL=postgres://profiling:profiling@localhost:5432/website_profil
export PYTHONPATH=src
```
-Start the server:
+Start the local stdio server:
```bash
python -m website_profiling.mcp
```
+For remote access over HTTP, see [Remote Streamable HTTP](#remote-streamable-http).
+
---
## Domain-scoped servers
@@ -111,6 +114,80 @@ Add to `.cursor/mcp.json` or your MCP client settings:
---
+## Remote Streamable HTTP
+
+Use this when Site Audit runs on a hosted server and your MCP client (Cursor, Claude Desktop, etc.) connects over the network instead of spawning a local stdio subprocess.
+
+### Start the HTTP server
+
+Configure access on **MCP settings** (`/mcp`) in the web UI (recommended), or set environment variables. UI changes apply on the next MCP request without restarting the service.
+
+```bash
+export DATABASE_URL=postgres://profiling:profiling@localhost:5432/website_profiling
+export PYTHONPATH=src
+export WP_MCP_HTTP_HOST=0.0.0.0
+export WP_MCP_HTTP_PORT=8000
+export WP_MCP_DOMAIN=core
+export WP_PROPERTY_ID=1
+
+python -m website_profiling.mcp.http
+```
+
+Set **MCP bearer token** and **Allowed hostnames** on the Secrets page (or via `WP_MCP_TOKEN` / `WP_MCP_ALLOWED_HOSTS`). Environment variables override saved values when set.
+
+The MCP endpoint is `http://{label}
+ {description ? ( +{description}
+ ) : null} +
+ {language === 'shell' ? `$ ${value}` : value}
+
+ {s.pageTitle}
+{s.pageSubtitle}
+{s.accessSubtitle}
+{publicUrl}/mcp
+
+ ) : null}
+ {s.copySubtitle}
++ {s.docsHint}{' '} + + {s.docsLink} + +
++ {s.mcpMovedHint}{' '} + + {s.mcpMovedLink} + + . +
+