Skip to content

Commit 4805e12

Browse files
PM-Labsclaude
andcommitted
Add CLAUDE.md: document the dual tool-registration-site gotcha
The 15-module registration bug fixed in 92ed21b happened because meta_ads_mcp/core/__init__.py and server.py's HTTP-transport import block are two separate registration sites, and only one (or neither) needs to be missed for a fully-merged, CI-passing module to be completely invisible as a live tool. Also documents the image-based GHCR deploy model (no droplet git clone, so the standard redeploy.mcp flow doesn't apply) and the GHCR org-transfer permission gotcha from the same incident. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012fUZk8PPzBSShxWTqa3ce5
1 parent 3d3b1b8 commit 4805e12

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

‎CLAUDE.md‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# mcp-meta-ads — Orientation
2+
3+
This is a Pathfinder-maintained fork of the open-source [Pipeboard Meta Ads MCP](https://github.com/pipeboard-co/meta-ads-mcp), deployed as a self-hosted remote MCP server (`PM_Facebook_Ads` / `meta-ads.mcp.pathfindermarketing.com.au`) rather than the upstream Pipeboard-hosted option. Registered in `pmtools/data/repos.json` as `mcp-meta-ads` (type `mcp`).
4+
5+
## Deploy model — image-based via GHCR, no droplet git clone
6+
7+
Unlike most Pathfinder MCPs, this one does **not** have a git clone on the droplet. CI (`.github/workflows/ci.yml`) builds and pushes the image to `ghcr.io/pmlabs-org/mcp-meta-ads:latest` on every push to `main`, then SSHes to the droplet and runs `docker compose pull meta-ads && docker compose up -d --force-recreate meta-ads`. Manual update if ever needed: `docker pull ghcr.io/pmlabs-org/mcp-meta-ads:latest && docker compose up -d --force-recreate meta-ads` on the droplet.
8+
9+
**Do not use the standard `pm.sub.ops.redeploy.mcp` git-clone-based flow for this repo** — it assumes `/opt/pmin-mcpinfrastructure/repos/mcp-<name>` exists, which it doesn't here. Verify a deploy landed by checking the specific GHA run's `deploy` job logs (see gotcha below), not just overall workflow status, and independently confirm via a direct `tools/list` JSON-RPC call against the live endpoint rather than trusting a green checkmark alone.
10+
11+
## Two tool-registration sites — both must be updated for a new module to actually work
12+
13+
This is the gotcha that caused a real incident (2026-08-12): 15 fully-built, CI-tested, merged tool modules sat completely invisible as MCP tools for ~4.5 months because they were merged (commits `a6f557a`, `0d42bf2`, `e2ddfc6`, 2026-03-31) but never wired into either registration site.
14+
15+
Adding a new file to `meta_ads_mcp/core/` with `@mcp_server.tool()`-decorated functions is **not enough on its own**. Both of these must be updated:
16+
17+
1. **`meta_ads_mcp/core/__init__.py`** — module-level imports (`from . import <module>`, following the existing pattern used by `ads_library`, `reports`, `duplication`, `authentication`). This registers the tools for stdio/package-level use and `pip install -e .` smoke tests.
18+
2. **`meta_ads_mcp/core/server.py`** — inside the `streamable-http` transport startup block (search for `"Ensuring all tools are registered for HTTP transport"`), there's a **separate, explicit** `from . import <module1>, <module2>, ...` list. **This is the one that actually matters for the deployed remote MCP** — `PM_Facebook_Ads` is served over `streamable-http`, and FastMCP only registers what gets imported in that code path at server startup.
19+
20+
A module can be present in the repo, pass CI, and ship in every built Docker image while being 100% invisible as a callable tool — because CI/tests only import the module directly, they don't exercise the `server.py` HTTP-transport startup path the same way the deployed server does. **Always verify via a live `tools/list` call after registering a new module, not just a passing test suite.**
21+
22+
## GHCR permissions can silently break on a GitHub org transfer
23+
24+
See `pmin-brain/context/tool-gotchas.md` § GitHub for the full incident writeup. Short version: this repo (and others) transferred from the `PM-Labs` org to `pmlabs-org`. The git remote still resolves (`git push` to the old `PM-Labs/...` URL succeeds via GitHub's redirect), but the GHCR container package's own "Manage Actions access" grant did not carry over — CI's `docker push` step 403'd on every run for about a week (2026-08-05 to 2026-08-11) despite correct `permissions: packages: write` in the workflow YAML. A separate `Test and Build` workflow (tests only, no push) kept reporting green the whole time, so the failure went unnoticed until someone checked the `deploy` job's logs specifically.
25+
26+
If a future push seems to succeed but the live server doesn't reflect the change, check the `deploy` job's actual `docker push` step output before assuming the code is wrong.
27+
28+
## Local clone convention
29+
30+
`$HOME/mcp-meta-ads` is a reference clone only — never edit it directly. All edits go through `pm.sub.ops.repo.edit`, which creates a fresh worktree rooted at `origin/main` for every edit, so this local clone's own branch state can drift without affecting anything (it is not kept in sync automatically the way `pmin-claude`'s dev clone is).

0 commit comments

Comments
 (0)