Skip to main content
Glama
README.md
# 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

A4.1/5.0

Scored across 10 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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.

Maintenance

ActivitySlowing
ResponsivenessNo issues