wcc_mcp_server
# WCC Pipeline MCP Server
Exposes the WCC event/mentorship/analytics pipeline as MCP tools over
streamable HTTP (or stdio for local testing). Built for SecondBrain's
agent-api MCP client, but any MCP client can connect.
One process serves **three tool groups**, each with its own endpoint, bearer
token, and lock — a token can only ever list/call its own group's tools:
| group | endpoint | tools | maps to skills |
|---|---|---|---|
| events | `/mcp/events` | 10 | everything except wcc-mentorship / wcc-analytic |
| mentorship | `/mcp/mentorship` | 8 | `skills/wcc-mentorship/` |
| analytic | `/mcp/analytic` | 2 | `skills/wcc-analytic/` |
## Layout
```
wcc_mcp/ MCP server package (tool table, runner, HTTP app)
tests/ pytest suite (also validates the vendored script closure)
skills/ vendored pipeline scripts, one folder per skill
skills/.env API secrets (NOT committed — copy skills/.env.example)
.env server config: group tokens, DRY_RUN (NOT committed)
```
`skills/` is a vendored snapshot of the WCC workspace skills (2026-07-22),
trimmed to the scripts the 20 tools actually execute (plus their transitive
helper scripts). The `skills/<skill>/scripts/` layout is preserved because the
scripts locate each other and `skills/.env` via relative paths. Vendored
changes vs. the workspace originals:
- `wcc-mentorship/scripts/publish_next_mentor.sh`: queue path is overridable
via `WCC_MENTOR_QUEUE` so the mutable queue can live on a Docker volume.
Browser automation (Meetup UI, HTML→PNG posters) is not part of this server;
`publish_event.sh` skips the Meetup step and tolerates missing posters.
## Local setup
```bash
cp .env.example .env # set WCC_MCP_TOKEN_* (openssl rand -hex 32)
cp skills/.env.example skills/.env # fill in API secrets
./run.sh # creates .venv, installs, serves http://127.0.0.1:8765/mcp/<group>
```
Host requirements: python3.10+, bash, jq, node (mentorship tools),
ffmpeg (`process_recording` only). Analytic tools additionally need
`pip install -r requirements-analytic.txt` (or set `WCC_ANALYTIC_PYTHON`
to a venv python that has pandas/seaborn).
### Modes
- **HTTP** (default): `./run.sh` — serves every group whose token is set;
clients send `Authorization: Bearer <group token>` to `/mcp/<group>`.
`/health` is open and reports per-group tool counts.
- **stdio**: `./run.sh --stdio [--group events|mentorship|analytic]` —
unauthenticated, one group at a time, for MCP Inspector or Claude Desktop:
`npx @modelcontextprotocol/inspector ./run.sh --stdio --group analytic`
### Dry runs
`DRY_RUN=true ./run.sh` passes `DRY_RUN=true` to every script — they log
instead of hitting Google/Luma/LinkedIn APIs. Always dry-run `publish_event`
after changing anything.
## Docker
```bash
docker build -t wcc-mcp .
docker run --rm --env-file .env \
-v "$PWD/skills/.env:/app/skills/.env:ro" \
-p 8765:8765 wcc-mcp
curl http://127.0.0.1:8765/health
```
The image bundles python + node deps and all vendored scripts. Mutable state
lives under `/data` (volume): analytics output (`WCC_ANALYTICS_DATA`) and the
mentor post queue (`WCC_MENTOR_QUEUE`). Draft-event state in `/tmp` is
ephemeral and lost on restart — same behavior as running on a host.
## Deployment (VM alongside agent-api)
The container is **not published on any host port**. It joins a shared Docker
network; agent-api on the same VM reaches it at `http://wcc-mcp:8765/mcp/<group>`.
One-time server bootstrap:
```bash
sudo mkdir -p /opt/wcc-mcp && cd /opt/wcc-mcp
# place docker-compose.yml (from this repo)
# place .env (from .env.example — real tokens; chmod 600)
# place skills.env (from skills/.env.example — real API secrets; chmod 600)
docker network create agent-net # agent-api compose must join it too
docker login ghcr.io # read-only PAT (private image)
docker compose up -d
# seed the mentor queue onto the volume:
docker compose cp wcc-mcp:/app/skills/wcc-mentorship/mentor_post_queue.txt.example /tmp/q.txt
docker compose exec wcc-mcp sh -c 'cat > /data/mentor_post_queue.txt' < /tmp/q.txt
```
In agent-api's `data/tenants.json`, point each tenant's MCP server at
`http://wcc-mcp:8765/mcp/<group>` with the matching bearer token
(`allow_private: true`). Alternatively, add the `wcc-mcp` service directly to
the agent-api compose stack instead of using the external network.
### Secrets model
- **Inbound auth**: per-group bearer tokens in `/opt/wcc-mcp/.env`; the only
client is agent-api on the internal Docker network — no TLS/reverse proxy
needed, nothing listens on a host interface.
- **Outbound auth**: API secrets in `/opt/wcc-mcp/skills.env`, mounted
read-only at `/app/skills/.env`. Never in the image, never in git, never in
GitHub Actions. Rotate by editing the file and `docker compose up -d`.
- CI only holds SSH deploy credentials (see below).
## CI/CD
`.github/workflows/ci-deploy.yml`: on push to `main` — pytest → build →
push `ghcr.io/<repo>:latest` + `:sha-…` → SSH to the VM →
`docker compose pull && up -d`.
Required GitHub repo secrets:
| secret | value |
|---|---|
| `SSH_HOST` | VM hostname/IP |
| `SSH_USER` | deploy user (in the `docker` group) |
| `SSH_KEY` | private key of a dedicated deploy keypair |
| `SSH_PORT` | optional, defaults to 22 |
## Pipeline rules (enforced via tool descriptions)
1. STOP on calendar conflicts — `quick_conflict_check` before drafting.
2. All event times are Europe/London.
3. Never `publish_event` before `set_registration_link` succeeded.
4. One event in flight at a time (`/tmp/draft_event.json`); exclusive tools
are serialized by a lock and fail fast if another step is running.
## Tests
```bash
pip install -e '.[dev]'
pytest
```
`test_every_script_exists_on_disk` guards the vendored closure — it fails if
a tool references a script that wasn't vendored.
TDQS
Scored across 10 tools
Each tool targets a distinct stage in the event lifecycle: pre-check, drafting, speaker lookup, registration, scheduling, publishing, post-event processing, and cleanup. Even set_registration_link and create_registration_form are clearly separated (one saves an existing link, the other creates a new form). No two tools are ambiguous.
Most tools follow a verb_noun pattern (draft_event, publish_event, collect_attendance, cleanup_state). Minor deviations exist: quick_conflict_check uses a modifier + noun_verb, and speaker_lookup has object_verb order. Overall the pattern is predictable and readable.
10 tools is well within the ideal range and each tool serves a necessary step in the event management workflow. The count feels intentionally scoped, covering planning through post-event actions without unnecessary bloat.
The surface covers the full event lifecycle: conflict checking, drafting, speaker lookup, registration, publishing, attendance, recording, and cleanup. Minor gaps exist (e.g., no update/cancel for published events, poster generation noted as not automated), but the core workflow is complete and agents can execute end-to-end.