diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 58685da..7c0785b 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -3,7 +3,7 @@ "name": "synter", "owner": { "name": "Synter", - "url": "https://syntermedia.ai" + "url": "https://synterai.com" }, "description": "Synter's official plugin marketplace. The AI Agent Operator for Ads.", "plugins": [ diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index ccc18e7..8ca4331 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -3,14 +3,14 @@ "name": "synter", "displayName": "Synter", "description": "Plan and operate advertising campaigns through Synter’s hosted MCP server over OAuth. Review performance, write ad copy, and approve campaign changes before execution.", - "version": "1.2.0", + "version": "1.2.1", "license": "MIT", "author": { "name": "Synter", "email": "joel@synterai.com", - "url": "https://syntermedia.ai" + "url": "https://synterai.com" }, - "homepage": "https://syntermedia.ai", + "homepage": "https://synterai.com", "repository": "https://github.com/Synter-Media-AI/plugin", "keywords": [ "ads", diff --git a/.cursor-mcp.json b/.cursor-mcp.json new file mode 100644 index 0000000..b865a6c --- /dev/null +++ b/.cursor-mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "synter": { + "type": "http", + "url": "https://mcp.synterai.com" + } + } +} diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 5850943..e07ddcb 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -6,7 +6,7 @@ }, "metadata": { "description": "Synter's official plugin marketplace. The AI Agent Operator for Ads.", - "version": "1.2.0" + "version": "1.2.1" }, "plugins": [ { diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 570e866..bf7e256 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -2,13 +2,13 @@ "$schema": "https://cursor.com/schemas/cursor-plugin/plugin.json", "name": "synter", "displayName": "Synter", - "version": "1.2.0", + "version": "1.2.1", "description": "Plan and operate advertising campaigns through Synter’s hosted MCP server over OAuth. Review performance, write ad copy, and approve campaign changes before execution.", "author": { "name": "Synter", "email": "joel@synterai.com" }, - "homepage": "https://syntermedia.ai", + "homepage": "https://synterai.com", "repository": "https://github.com/Synter-Media-AI/plugin", "license": "MIT", "logo": "assets/logo.png", @@ -29,5 +29,5 @@ "skills": "./skills/", "agents": "./agents/", "rules": "./rules/", - "mcpServers": "./mcp.json" + "mcpServers": "./.cursor-mcp.json" } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f5e827d..87a45ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,16 +31,29 @@ jobs: run: node --test scripts/*.test.js - name: Run strict Claude plugin validation - run: npx -y @anthropic-ai/claude-code@2.1.263 plugin validate . --strict - - - name: Ensure zip/rsync are available run: | - command -v zip >/dev/null || sudo apt-get update -qq && sudo apt-get install -y -qq zip - command -v rsync >/dev/null || sudo apt-get update -qq && sudo apt-get install -y -qq rsync + npx -y @anthropic-ai/claude-code@2.1.263 plugin validate . --strict + npx -y @anthropic-ai/claude-code@2.1.263 plugin validate .claude-plugin/plugin.json --strict + python3 scripts/build-packages.py --check-claude-directory + npx -y @anthropic-ai/claude-code@2.1.263 plugin validate packages/claude-directory --strict + + - name: Verify release tag matches the package + env: + RELEASE_TAG: ${{ github.ref_name }} + run: node -e 'if (process.env.RELEASE_TAG !== "synter--v" + require("./.claude-plugin/plugin.json").version) process.exit(1)' + + - name: Test archive packaging + run: python3 -m unittest discover -s scripts -p 'test_*.py' -v - name: Build plugin zip run: bash scripts/build-zip.sh + - name: Validate extracted Claude package + run: | + VERSION=$(node -p "require('./.claude-plugin/plugin.json').version") + python3 -m zipfile -e "dist/synter-claude-plugin-$VERSION.zip" /tmp/synter-claude-package + npx -y @anthropic-ai/claude-code@2.1.263 plugin validate /tmp/synter-claude-package/.claude-plugin/plugin.json --strict + - name: Resolve version id: version run: echo "version=$(node -p "require('./.claude-plugin/plugin.json').version")" >> "$GITHUB_OUTPUT" @@ -48,5 +61,10 @@ jobs: - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: - files: dist/synter-plugin-${{ steps.version.outputs.version }}.zip + files: | + dist/synter-claude-plugin-${{ steps.version.outputs.version }}.zip + dist/synter-claude-directory-plugin-${{ steps.version.outputs.version }}.zip + dist/synter-openai-plugin-${{ steps.version.outputs.version }}.zip + dist/*.sha256 + dist/package-manifest.json generate_release_notes: true diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index cedc124..76f2137 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -20,4 +20,26 @@ jobs: run: node scripts/validate-manifests.js - name: Run strict Claude plugin validation - run: npx -y @anthropic-ai/claude-code@2.1.263 plugin validate . --strict + run: | + npx -y @anthropic-ai/claude-code@2.1.263 plugin validate . --strict + npx -y @anthropic-ai/claude-code@2.1.263 plugin validate .claude-plugin/plugin.json --strict + python3 scripts/build-packages.py --check-claude-directory + npx -y @anthropic-ai/claude-code@2.1.263 plugin validate packages/claude-directory --strict + + - name: Test package validation + run: | + node --test scripts/*.test.js + python3 -m unittest discover -s scripts -p 'test_*.py' -v + bash scripts/build-zip.sh --include-new-draft + + - name: Validate extracted Claude package + run: | + VERSION=$(node -p "require('./.claude-plugin/plugin.json').version") + python3 -m zipfile -e "dist/synter-claude-plugin-$VERSION.zip" /tmp/synter-claude-package + npx -y @anthropic-ai/claude-code@2.1.263 plugin validate /tmp/synter-claude-package/.claude-plugin/plugin.json --strict + + - name: Save reviewable package artifacts + uses: actions/upload-artifact@v4 + with: + name: synter-plugin-packages + path: dist/ diff --git a/.mcp.json b/.mcp.json index 91f0211..c3ae094 100644 --- a/.mcp.json +++ b/.mcp.json @@ -2,7 +2,7 @@ "mcpServers": { "synter": { "type": "http", - "url": "https://mcp.syntermedia.ai", + "url": "https://mcp.synterai.com", "description": "Synter Advertising Platform MCP \u2014 cross-channel ad management for AI agents. Sign in through the browser OAuth prompt; never paste credentials into chat." } } diff --git a/CHANGELOG.md b/CHANGELOG.md index c5fb8ce..7c7e76b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [1.2.1] - Unreleased + +- Add a portable OpenAI package targeting the existing Synter Ads MCP draft, + with directory metadata, OAuth discovery, and reviewer test cases. +- Preserve the four existing creative, page, visibility, and creator-brief skill + entry points with scopes limited to available tools and explicit production gaps. +- Use canonical Synter domains in active client manifests and onboarding links. +- Build separate Claude and OpenAI archives from explicit runtime files, verify + every archived byte, and emit source identity and SHA-256 checksums. +- Keep the Claude session primer out of the public OpenAI archive. + + All notable changes to the Synter plugin are documented here. This project follows [semantic versioning](https://semver.org). ## [1.2.0] - 2026-10-02 diff --git a/README.md b/README.md index 39ea287..8242c90 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,45 @@ Synter

-# Synter — Claude and Cursor plugin +# Synter plugin Plan advertising campaigns, review performance, write ad copy, and apply approved campaign changes through Synter. This plugin is a no-cost client for the paid Synter service. You need a Synter account and authorized advertising accounts to use the hosted tools. ## Install and sign in +### ChatGPT and Codex + +The OpenAI package includes Synter skills and the hosted OAuth MCP connection. +Its generated `mcp.json` declares OAuth with dynamic client registration under +`mcpServers.synter.extensions.com.openai.auth`; discovery provides the endpoints. +The portal displays these settings and does not provide an advanced OAuth editor. +The existing-draft archive is `dist/synter-openai-plugin-1.2.1.zip`, for +**Synter Ads MCP**. Its package identity remains +`app-6a90dc5bbc3c819199b4f1e44028fcbf`. Uploading creates a draft; directory +approval and publication are separate steps. This archive contains no lifecycle +hooks or local executables. + +The existing draft currently rejects replacement ZIPs with “Configure the existing +MCP app before updating its plugin ZIP,” while its connection settings are +unavailable. A valid archive does not resolve that portal state. Verify the saved +MCP origin before updating: OpenAI does not allow an origin change between versions. +`bash scripts/build-zip.sh --include-new-draft` also prepares +`dist/synter-openai-new-draft-plugin-1.2.1.zip` with the canonical `synter` package +name for a separately authorized new draft if the existing identity cannot be +recovered. Building it does not upload, register, or publish anything. + +### Claude Desktop and Cowork + +Build the archives with `bash scripts/build-zip.sh`, then use Claude Desktop's +plugin upload to select `dist/synter-claude-plugin-1.2.1.zip`. The archive contains +the `.claude-plugin` manifest, OAuth configuration, skills, agents, and static +session primer. Start a new conversation after updating and complete browser +OAuth when prompted. Uploading a ZIP does not enable automatic GitHub updates. You can also add +`Synter-Media-AI/plugin` as a repository marketplace in **Customize → Plugins** +and install `synter`. Direct installation does not establish public directory +approval. The current GitHub release may be older than this source package. + + ### Claude Code ```text @@ -17,12 +50,6 @@ Plan advertising campaigns, review performance, write ad copy, and apply approve Start a new conversation and run `/synter:quickstart`. Complete Synter’s browser OAuth prompt when the client requests authorization. Never paste credentials, access tokens, or authorization codes into chat. -### Claude Desktop and Cowork - -In **Customize → Plugins**, add `Synter-Media-AI/plugin` as a repository marketplace and install `synter`. You can also upload the plugin zip from the [latest GitHub release](https://github.com/Synter-Media-AI/plugin/releases/latest). Start a new conversation and complete the browser OAuth prompt. - -Direct installation does not imply that Anthropic has approved a public directory listing. - ### Cursor Import `https://github.com/Synter-Media-AI/plugin` through your team’s plugin marketplace, or symlink this repository into `~/.cursor/plugins/local/synter` and reload Cursor. Complete the browser OAuth prompt for Synter, then run `/synter:quickstart`. @@ -31,10 +58,10 @@ Both plugin configurations use remote HTTP MCP with browser OAuth. They contain | Client | Configuration | Hosted MCP URL | | --- | --- | --- | -| Cursor | `mcp.json`, referenced by `.cursor-plugin/plugin.json` | `https://mcp.synterai.com` | -| Claude | `.mcp.json`, discovered by the Claude plugin | `https://mcp.syntermedia.ai` | +| Cursor | `.cursor-mcp.json`, referenced by `.cursor-plugin/plugin.json` | `https://mcp.synterai.com` | +| Claude | `.mcp.json`, discovered by the Claude plugin | `https://mcp.synterai.com` | -Claude retains the connector URL under Anthropic review. Keep the two configurations separate. For another remote MCP client, use its browser OAuth flow with the appropriate hosted URL. If sign-in fails, reconnect through the client’s settings or contact [support](https://syntermedia.ai/support). +The portable base `mcp.json` uses the Agent Plugins schema and `streamable-http` transport. The OpenAI build adds its documented server authentication extension; the published portable 1.0.0 schema currently rejects that extension, so validate the base against that schema and the generated vendor settings against the documented contract separately. Claude and Cursor use their own HTTP configuration files. All generated packages connect to the canonical endpoint. Existing OpenAI connections on another origin require a new plugin or support-assisted recovery, not a blind endpoint replacement. For another remote MCP client, use its browser OAuth flow with the appropriate hosted URL. If sign-in fails, reconnect through the client’s settings or contact [support](https://synterai.com/contact). ## What this plugin runs @@ -42,10 +69,12 @@ The plugin connects to the hosted Synter MCP server over OAuth. The advertising Claude also runs one `SessionStart` hook from [.claude-plugin/plugin.json](.claude-plugin/plugin.json). It invokes [scripts/session-context.sh](scripts/session-context.sh), a Bash script that uses `cat` to print static account-scope and approval guidance. It does not read credentials or make network requests. Cursor declares no hooks and uses [rules/synter-operator.mdc](rules/synter-operator.mdc). -There is no local MCP server or bundled compiled binary. The repository includes an optional [Node SDK runner](sdk/README.md), invoked manually, and development scripts for validation and packaging. The release zip excludes the SDK. Agent definitions may delegate work when the host client supports them. +There is no local MCP server or bundled compiled binary. The repository includes an optional [Node SDK runner](sdk/README.md), invoked manually, and development scripts for validation and packaging. Both distribution archives exclude the SDK, local configuration, and development scripts. Agent definitions may delegate work when the host client supports them. Bundled skills (invoke as `/synter:`): +- [`creative`](skills/creative/SKILL.md) · [`landing-page-creation`](skills/landing-page-creation/SKILL.md) · [`llm-visibility-audit`](skills/llm-visibility-audit/SKILL.md) · [`ugc-creative-brief`](skills/ugc-creative-brief/SKILL.md) + - [`ad-copy-generation`](skills/ad-copy-generation/SKILL.md) · [`ad-policy-compliance`](skills/ad-policy-compliance/SKILL.md) · [`anomaly-detector`](skills/anomaly-detector/SKILL.md) · [`attribution`](skills/attribution/SKILL.md) - [`audience`](skills/audience/SKILL.md) · [`audience-expansion-strategy`](skills/audience-expansion-strategy/SKILL.md) · [`bid-optimization`](skills/bid-optimization/SKILL.md) · [`campaign-preflight`](skills/campaign-preflight/SKILL.md) - [`campaign-structure-auditor`](skills/campaign-structure-auditor/SKILL.md) · [`competitor-analysis`](skills/competitor-analysis/SKILL.md) · [`connect`](skills/connect/SKILL.md) · [`conversion-tracking`](skills/conversion-tracking/SKILL.md) @@ -62,7 +91,7 @@ Bundled skills (invoke as `/synter:`): Bundled agents: [`audience-builder`](agents/audience-builder.md) · [`budget-optimizer`](agents/budget-optimizer.md) · [`campaign-strategist`](agents/campaign-strategist.md) · [`creative-director`](agents/creative-director.md) · [`media-buyer`](agents/media-buyer.md) · [`performance-analyst`](agents/performance-analyst.md). -[Privacy policy](https://syntermedia.ai/privacy) · [Support](https://syntermedia.ai/support) +[Privacy policy](https://synterai.com/privacy) · [Support](https://synterai.com/contact) ## Campaign safety and scope @@ -77,7 +106,8 @@ Text ads use `create_ad_copy`; existing assets use `list_creative_assets`. The p ```bash node scripts/validate-manifests.js node scripts/check-directory-tools.js -node --test scripts/check-directory-tools.test.js +node --test scripts/*.test.js +python3 -m unittest discover -s scripts -p 'test_*.py' -v npx -y @anthropic-ai/claude-code@2.1.263 plugin validate . --strict bash scripts/build-zip.sh ``` @@ -86,4 +116,37 @@ The directory check scans every file under `skills/`, `commands/` (when present) The check runs offline in CI. To also compare the directory against live MCP `tools/list`, supply an OAuth access token through `SYNTER_MCP_TOKEN` in your environment and run the same script. It initializes an MCP session and follows paginated results. `--live` requires the token instead of silently running offline. `SYNTER_MCP_URL` optionally selects either hosted URL in the table; it defaults to the Cursor endpoint. The token is for this development check only and is never written to plugin configuration. Extra live tools do not expand the checked-in directory. -[Terms](https://syntermedia.ai/terms) · [MIT license](LICENSE) +[Terms](https://synterai.com/terms) · [MIT license](LICENSE) + + +## Package verification and release + +The build uses an explicit runtime file allowlist and rejects symlinks. It reads +back every ZIP entry and checks exact bytes, writes SHA-256 sidecars, and records +source commit and dirty-worktree status in `dist/package-manifest.json`. The +OpenAI archive uses the existing app identity from `config/openai-submission.json`; +the Claude archive keeps the `synter` identity. Their manifests and transport +formats are intentionally separate. + +```mermaid +flowchart LR + A["Reviewed source"] --> B["Claude archive"] + A --> C["OpenAI archive"] + B --> D["Exact content verification"] + C --> D + D --> E["Portal upload and validation"] + E --> F["Directory review"] + F --> G["Approved publication"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E,F,G step; +``` + +Current package checks do not prove a successful authenticated host install, +provider tool execution, or public directory approval. OpenAI MCP submission +also requires a reviewer-accessible demo recording, a working reviewer account, +and successful domain and tool checks in the portal. The packaged test cases +are review instructions, not claims that those live checks already passed. + +Format references: [OpenAI package guide](https://developers.openai.com/plugins/build/plugins), +[OpenAI submission guide](https://developers.openai.com/plugins/deploy/submission), +and [Claude manifest reference](https://code.claude.com/docs/en/plugins-reference). diff --git a/SECURITY.md b/SECURITY.md index 6d5bf4d..fdaddff 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,7 @@ Email **joel@synterai.com** with the subject "Security: Synter plugin". Please i ## What this plugin does -The plugin ships Markdown skills, agents, and rules. Cursor connects to `https://mcp.synterai.com`; Claude connects to `https://mcp.syntermedia.ai`. Both use browser OAuth without static credential headers or secret environment configuration. +The plugin ships Markdown skills, agents, and rules. ChatGPT, Codex, Claude, and Cursor connect to `https://mcp.synterai.com` with browser OAuth without static credential headers or secret environment configuration. Claude runs one SessionStart Bash hook that prints static guidance through `cat`. It does not read credentials or make network requests. Cursor declares no executable hooks. The optional Node SDK runner and repository validation/build scripts run only when invoked manually; no compiled binaries are bundled. @@ -17,4 +17,4 @@ Claude runs one SessionStart Bash hook that prints static guidance through `cat` ## Data handling -For service data handling, see the [Privacy Policy](https://syntermedia.ai/privacy), [Terms](https://syntermedia.ai/terms), and [Security overview](https://syntermedia.ai/security). +For service data handling, see the [Privacy Policy](https://synterai.com/privacy), [Terms](https://synterai.com/terms), and [Security overview](https://synterai.com/security). diff --git a/agents/creative-director.md b/agents/creative-director.md index d7a63f8..dfcec8c 100644 --- a/agents/creative-director.md +++ b/agents/creative-director.md @@ -15,6 +15,6 @@ Method: Non-negotiables: - Use the verified advertiser’s brand kit, voice, approved claims, and original assets. Synter’s own brand rules apply only when Synter is the advertiser. - Accuracy over polish: never fabricate a price, competitor, statistic, or claim. No real number → say so; don't invent it. -- Fill the format: RSAs ship with 11+ headlines and 4 descriptions (15/4 recommended, never the API minimum of 3/2), with the ad group's actual keywords echoed in at least 5 headlines — under-filled asset sets score "Poor" on Ad Strength and get throttled. No filler lines; rewrite to the character limit rather than truncate. +- Fit the format: use concise, distinct RSA headlines that match search intent and deliberately qualify the intended audience. Meet the platform’s required counts and character limits; do not impose an 11-headline floor or pad to 15. Ad Strength is not a launch gate. Rewrite overlong lines instead of truncating them. - Policy: pre-check against the target platform's ad rules and flag anything restricted. - Writing text does not deploy it. Adding assets to a live campaign requires an approved scope and the **launch** workflow. diff --git a/agents/media-buyer.md b/agents/media-buyer.md index ccaeca8..f6e132a 100644 --- a/agents/media-buyer.md +++ b/agents/media-buyer.md @@ -11,7 +11,7 @@ Discipline: - Confirm the target before touching anything: `list_connected_accounts`. Use the exact account IDs the tools return — never invent or guess an ID. Never run one org's campaign through another org's key. - Check available conversion evidence with `ga4_get_conversions`, `ga4_get_report`, and `get_pixel_health`. Stop if required tracking verification is missing. - Run preflight every time: geo correct, exclusions applied, tracking firing, budget and bid caps sane (guard hard against fat-finger budgets — a daily budget 10–100x intended is the classic incident), creative policy- and brand-clean, final URL resolves and matches the ad. -- Creative floor before launch: every RSA carries 11+ headlines and 4 descriptions (15/4 recommended — never the API minimum of 3/2), keywords echoed in the headlines. Google Ad Strength POOR or AVERAGE means fix the assets first, not launch and hope. +- Before launch, verify RSA assets meet the platform’s required counts and character limits. Prefer fewer concise, relevant headlines that qualify the intended audience and communicate the offer. Never pad headlines to reach 15. Ad Strength is not a launch gate. - Build with `create_campaign_plan` and `upsert_plan_entity`. Read `get_campaign_plan`, run `run_launch_preflight`, show the final scope, obtain explicit user approval, then call `approve_campaign_plan` and `execute_campaign_plan`. Check `get_plan_execution` and `get_job_status` for returned jobs; use `enable_campaign` only within the approved scope. - When porting campaigns, read `list_campaigns` and `audit_account_structure`; request platform exports for missing geo, match-type, negative, and asset details. Follow **replicate** and the target platform playbook. diff --git a/config/claude-directory/README.md b/config/claude-directory/README.md new file mode 100644 index 0000000..dfae0cd --- /dev/null +++ b/config/claude-directory/README.md @@ -0,0 +1,20 @@ +# Synter + +Synter helps you review advertising performance, prepare campaign plans, write ad copy, and approve changes through its hosted MCP service. Start with the quickstart skill, sign in through the browser OAuth prompt, and verify the connected workspace and advertising account before using account data. + +This directory package includes 56 Markdown skills, six agent instructions, an output style, and static [brand and safety guidance](context/brand-and-safety.md). Its only service connection is the remote HTTPS MCP server declared in `.mcp.json`. It contains no local lifecycle hooks, executable scripts, or SDK runner. Tool availability follows the bundled [tool directory](tools/directory-tools.json) and the live server schema. + +Read-only account lookups and reports can run directly. Before any campaign creation, activation, pause, budget adjustment, or other spend-changing action, review the proposed account, scope, and amounts and give explicit approval. After execution, check the provider readback before treating a change as complete. Use the verified advertiser's brand and original assets; do not substitute Synter's branding for another advertiser. + +```mermaid +flowchart LR + User["Your request and approval"] --> Claude["Claude with Synter instructions"] + Claude --> MCP["Synter HTTPS MCP over OAuth"] + MCP --> Provider["Your connected advertising accounts"] + Provider --> Readback["Results and verification"] + Readback --> Claude +``` + +Tool arguments and account results pass through Synter's hosted service and the connected advertising provider. Never paste passwords, API keys, or access tokens into chat. Sample data must be identified as sample data; missing account access or unavailable metrics must be reported plainly. Read [Synter's privacy policy](https://synterai.com/privacy) for data handling and [terms](https://synterai.com/terms) for service conditions. + +For help, use the help skill or [contact Synter](https://synterai.com/contact). This folder is generated from reviewed repository sources and is intended for the Claude directory submission path `packages/claude-directory`. Directory approval and publication are separate from local package validation. The source is distributed under the included MIT license. diff --git a/config/claude-directory/help.md b/config/claude-directory/help.md new file mode 100644 index 0000000..f4f76c6 --- /dev/null +++ b/config/claude-directory/help.md @@ -0,0 +1,14 @@ +--- +name: help +description: Explain the bundled skills, supported MCP tools, OAuth setup, and support options. +--- + +# Synter help + +This plugin connects to Synter's hosted advertising service over OAuth. Use **quickstart** to sign in and **connect** to verify accounts. + +Use **report** and **attribution** to review performance, **media-plan** and **launch** to prepare approved campaign changes, **audience** to inspect targeting, and **ad-copy-generation** to write text for the verified advertiser. Read [brand and safety guidance](../../context/brand-and-safety.md) before writing copy or proposing spend. + +This directory package uses static instructions and the declared remote MCP connection. Account operations run through Synter MCP tools. Use only tools in the bundled [tool directory](../../tools/directory-tools.json) and the live server schema; report unavailable capabilities plainly. Confirm the workspace and account, obtain explicit approval for spend-changing actions, and verify provider readback before claiming completion. + +[Privacy](https://synterai.com/privacy) · [Support](https://synterai.com/contact) diff --git a/config/openai-submission.json b/config/openai-submission.json new file mode 100644 index 0000000..752a0e8 --- /dev/null +++ b/config/openai-submission.json @@ -0,0 +1,6 @@ +{ + "pluginId": "plugin_asdk_app_6a90dc5bbc3c819199b4f1e44028fcbf", + "packageName": "app-6a90dc5bbc3c819199b4f1e44028fcbf", + "displayName": "Synter Ads MCP", + "developerName": "JOEL SAMUEL HORWITZ" +} diff --git a/mcp.json b/mcp.json index b865a6c..4a918a7 100644 --- a/mcp.json +++ b/mcp.json @@ -1,7 +1,8 @@ { + "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", "mcpServers": { "synter": { - "type": "http", + "type": "streamable-http", "url": "https://mcp.synterai.com" } } diff --git a/packages/claude-directory/.claude-plugin/plugin.json b/packages/claude-directory/.claude-plugin/plugin.json new file mode 100644 index 0000000..8004b46 --- /dev/null +++ b/packages/claude-directory/.claude-plugin/plugin.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", + "name": "synter", + "displayName": "Synter", + "description": "Plan and operate advertising campaigns through Synter’s hosted MCP server over OAuth. Review performance, write ad copy, and approve campaign changes before execution.", + "version": "1.2.1", + "license": "MIT", + "author": { + "name": "Synter", + "email": "joel@synterai.com", + "url": "https://synterai.com" + }, + "homepage": "https://synterai.com", + "repository": "https://github.com/Synter-Media-AI/plugin", + "keywords": [ + "ads", + "advertising", + "campaigns", + "audiences", + "media-buying", + "creative", + "roas", + "google-ads", + "meta-ads", + "linkedin-ads", + "mcp", + "synter" + ] +} diff --git a/packages/claude-directory/.mcp.json b/packages/claude-directory/.mcp.json new file mode 100644 index 0000000..c3ae094 --- /dev/null +++ b/packages/claude-directory/.mcp.json @@ -0,0 +1,9 @@ +{ + "mcpServers": { + "synter": { + "type": "http", + "url": "https://mcp.synterai.com", + "description": "Synter Advertising Platform MCP \u2014 cross-channel ad management for AI agents. Sign in through the browser OAuth prompt; never paste credentials into chat." + } + } +} diff --git a/packages/claude-directory/LICENSE b/packages/claude-directory/LICENSE new file mode 100644 index 0000000..0b3cf68 --- /dev/null +++ b/packages/claude-directory/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Synter (Syntermedia, Inc.) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/claude-directory/README.md b/packages/claude-directory/README.md new file mode 100644 index 0000000..dfae0cd --- /dev/null +++ b/packages/claude-directory/README.md @@ -0,0 +1,20 @@ +# Synter + +Synter helps you review advertising performance, prepare campaign plans, write ad copy, and approve changes through its hosted MCP service. Start with the quickstart skill, sign in through the browser OAuth prompt, and verify the connected workspace and advertising account before using account data. + +This directory package includes 56 Markdown skills, six agent instructions, an output style, and static [brand and safety guidance](context/brand-and-safety.md). Its only service connection is the remote HTTPS MCP server declared in `.mcp.json`. It contains no local lifecycle hooks, executable scripts, or SDK runner. Tool availability follows the bundled [tool directory](tools/directory-tools.json) and the live server schema. + +Read-only account lookups and reports can run directly. Before any campaign creation, activation, pause, budget adjustment, or other spend-changing action, review the proposed account, scope, and amounts and give explicit approval. After execution, check the provider readback before treating a change as complete. Use the verified advertiser's brand and original assets; do not substitute Synter's branding for another advertiser. + +```mermaid +flowchart LR + User["Your request and approval"] --> Claude["Claude with Synter instructions"] + Claude --> MCP["Synter HTTPS MCP over OAuth"] + MCP --> Provider["Your connected advertising accounts"] + Provider --> Readback["Results and verification"] + Readback --> Claude +``` + +Tool arguments and account results pass through Synter's hosted service and the connected advertising provider. Never paste passwords, API keys, or access tokens into chat. Sample data must be identified as sample data; missing account access or unavailable metrics must be reported plainly. Read [Synter's privacy policy](https://synterai.com/privacy) for data handling and [terms](https://synterai.com/terms) for service conditions. + +For help, use the help skill or [contact Synter](https://synterai.com/contact). This folder is generated from reviewed repository sources and is intended for the Claude directory submission path `packages/claude-directory`. Directory approval and publication are separate from local package validation. The source is distributed under the included MIT license. diff --git a/packages/claude-directory/agents/audience-builder.md b/packages/claude-directory/agents/audience-builder.md new file mode 100644 index 0000000..f743617 --- /dev/null +++ b/packages/claude-directory/agents/audience-builder.md @@ -0,0 +1,20 @@ +--- +name: audience-builder +description: Reviews existing audiences and plans targeting — ABM criteria, lookalike seeds, first-party segments, and exclusions — for supported campaign-plan fields. Invoke when a user needs to define who to reach. List creation and sync happen in the platform UI. +model: inherit +effort: high +--- + +You turn 'who we want to reach' into a reviewed targeting brief using existing audience IDs. + +Approach: +- Inspect existing audiences with `list_audiences`; define account criteria, seeds, exclusions, and consent requirements. Use `research_campaign_opportunity` for available research. Have the account owner construct or synchronize new lists in the platform UI. +- Check `list_audiences` first — never rebuild a duplicate. +- Ask the account owner for size and match-rate figures from the platform; check overlap and whether the audience clears each platform's minimum (LinkedIn company audiences and Reddit have real floors). Report the numbers honestly — a sub-floor segment won't deliver, and you say so. +- First-party data: the advertiser hashes and uploads in the platform; tiny B2B work-email lists often miss the match floor — warn the user. +- Confirm returned audience IDs, then hand the reviewed targeting brief to the media-buyer for supported campaign-plan fields and approval. + +Rules: +- Confirm the org/account before using an audience in a campaign. Reads do not require approval; using an audience in a campaign is an action. +- Respect match-rate floors — surface them, never hide a too-small segment. +- Handle PII correctly: have the advertiser hash identifiers where the platform expects hashes; never leak one tenant's data into another. diff --git a/packages/claude-directory/agents/budget-optimizer.md b/packages/claude-directory/agents/budget-optimizer.md new file mode 100644 index 0000000..55da617 --- /dev/null +++ b/packages/claude-directory/agents/budget-optimizer.md @@ -0,0 +1,22 @@ +--- +name: budget-optimizer +description: Finds wasted spend and winners across platforms, then reallocates budget by ROAS, pauses losers, and adjusts budgets toward winners. Invoke when a user wants to cut waste, improve ROAS/CPA, or rebalance spend. Recommends, then acts on approval. +model: inherit +effort: high +--- + +You are Synter's budget optimizer. You move money toward what works and away from what doesn't — on the user's approval. + +Method: +- Pull recent performance across connected platforms using the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`), reconcile with `get_spend_reconciliation`, and read true drivers with `get_attribution`. +- Diagnose against minimum-data guardrails — never kill or scale on noise. State the threshold you used. + - Wasted spend: spend with no conversions past the threshold. + - Winners: low CPA / high ROAS with enough volume to scale. + - Anomalies: spend spikes, CTR drops, budget runaway. + - Fatigue: rising frequency + falling CTR → flag for creative rotation. +- Use `optimize_budget` for allocation recommendations. Apply approved budget changes with `update_campaign_budget` and approved pauses with `pause_campaign`. For other changes, follow **launch** or hand off unsupported edits to the platform UI. + +Rules: +- Recommend first: what to cut, what to scale, where freed budget goes, expected effect. Wait for explicit approval before any budget/pause/bid change. +- After acting, re-pull and report the new state. Recommend alert thresholds for the user to configure in the platform UI. +- Never report a change as done without confirming it landed. diff --git a/packages/claude-directory/agents/campaign-strategist.md b/packages/claude-directory/agents/campaign-strategist.md new file mode 100644 index 0000000..d75ac7e --- /dev/null +++ b/packages/claude-directory/agents/campaign-strategist.md @@ -0,0 +1,19 @@ +--- +name: campaign-strategist +description: Plans cross-platform advertising strategy — channel mix, budget allocation, audience approach, and measurement. Invoke when a user needs a media plan, a go-to-market for a launch, or a recommendation on where and how to spend before any campaign is built. +model: opus +effort: high +--- + +You are Synter's campaign strategist. You turn a business goal into a concrete, defensible media plan. You plan; you do not spend — building and launching is the media-buyer's job, and the user approves it. + +How you work: +- Start from the objective and the conversion that defines success, not from a platform. Ask only for what you genuinely need: goal, target CPA/ROAS, budget, audience, geo, offer. +- Recommend the channel mix with a real rationale grounded in how each platform performs for this objective and audience. Don't just echo the platform the user named. +- Ground the proposal in `list_connected_accounts`, `research_campaign_opportunity`, `forecast_campaign`, and the supported platform performance tools. Separate historical observations from estimates and unavailable data. +- Allocate budget across channels with projected outcomes per channel. Show the math. Start conservative; plan the scale path. +- Always include measurement: which conversion, which tracking, how you'll know it worked. + +Ground platform choices in how each one actually works — the **platform-google**, **platform-microsoft**, **platform-meta**, **platform-linkedin**, and sibling playbook skills carry the structure, targeting, and cost realities per platform. For a full channel-mix plan, the **media-plan** skill is the build path. + +Output a tight plan: objective → audience → channel mix + budget split → projected CPA/ROAS → creative direction → measurement → first 2-week test. Lead with the recommendation, then the reasoning. Terse and certain. No hype, no banned marketing language. diff --git a/packages/claude-directory/agents/creative-director.md b/packages/claude-directory/agents/creative-director.md new file mode 100644 index 0000000..dfcec8c --- /dev/null +++ b/packages/claude-directory/agents/creative-director.md @@ -0,0 +1,20 @@ +--- +name: creative-director +description: Writes ad copy, reviews existing advertiser assets, and prepares test briefs. Use for text variants, asset selection, and campaign creative review. +model: opus +effort: high +--- + +You are Synter's creative director. You produce ad creative that is on-brand, accurate, and ready to ship. + +Method: +- Get the brief: platform + format, objective, audience, the one message, and the real proof point. Read brand rules at `${CLAUDE_PLUGIN_ROOT}/context/brand-and-safety.md` before writing anything. +- Write text with `create_ad_copy`; prepare variants that test a specific hypothesis. Request missing media from the advertiser. +- Inspect existing assets with `list_creative_assets`. Use returned asset IDs in supported campaign-plan fields and follow the launch approval flow. + +Non-negotiables: +- Use the verified advertiser’s brand kit, voice, approved claims, and original assets. Synter’s own brand rules apply only when Synter is the advertiser. +- Accuracy over polish: never fabricate a price, competitor, statistic, or claim. No real number → say so; don't invent it. +- Fit the format: use concise, distinct RSA headlines that match search intent and deliberately qualify the intended audience. Meet the platform’s required counts and character limits; do not impose an 11-headline floor or pad to 15. Ad Strength is not a launch gate. Rewrite overlong lines instead of truncating them. +- Policy: pre-check against the target platform's ad rules and flag anything restricted. +- Writing text does not deploy it. Adding assets to a live campaign requires an approved scope and the **launch** workflow. diff --git a/packages/claude-directory/agents/media-buyer.md b/packages/claude-directory/agents/media-buyer.md new file mode 100644 index 0000000..f6e132a --- /dev/null +++ b/packages/claude-directory/agents/media-buyer.md @@ -0,0 +1,21 @@ +--- +name: media-buyer +description: Builds and launches campaigns across platforms once a plan is approved — structure, targeting, budgets, and going live. Invoke to execute a launch, ship a campaign to one or more platforms, or enable a paused campaign. Always confirms before spending. +model: inherit +effort: high +--- + +You are Synter's media buyer. You take an approved plan and make it live, correctly and safely. You execute; the user approves every spend. + +Discipline: +- Confirm the target before touching anything: `list_connected_accounts`. Use the exact account IDs the tools return — never invent or guess an ID. Never run one org's campaign through another org's key. +- Check available conversion evidence with `ga4_get_conversions`, `ga4_get_report`, and `get_pixel_health`. Stop if required tracking verification is missing. +- Run preflight every time: geo correct, exclusions applied, tracking firing, budget and bid caps sane (guard hard against fat-finger budgets — a daily budget 10–100x intended is the classic incident), creative policy- and brand-clean, final URL resolves and matches the ad. +- Before launch, verify RSA assets meet the platform’s required counts and character limits. Prefer fewer concise, relevant headlines that qualify the intended audience and communicate the offer. Never pad headlines to reach 15. Ad Strength is not a launch gate. +- Build with `create_campaign_plan` and `upsert_plan_entity`. Read `get_campaign_plan`, run `run_launch_preflight`, show the final scope, obtain explicit user approval, then call `approve_campaign_plan` and `execute_campaign_plan`. Check `get_plan_execution` and `get_job_status` for returned jobs; use `enable_campaign` only within the approved scope. +- When porting campaigns, read `list_campaigns` and `audit_account_structure`; request platform exports for missing geo, match-type, negative, and asset details. Follow **replicate** and the target platform playbook. + +Hard rules: +- Recommend-then-execute: propose the plan (platform, budget, audience, creative), show it, and wait for an explicit yes before any create/enable/budget/launch/pause call. Reads do not require approval. +- "Created" ≠ "live and spending." Verify with `list_campaigns` and report the real IDs before claiming it's running. +- Start conservative on budget. Scaling is a later, separate execution step. diff --git a/packages/claude-directory/agents/performance-analyst.md b/packages/claude-directory/agents/performance-analyst.md new file mode 100644 index 0000000..c6ed649 --- /dev/null +++ b/packages/claude-directory/agents/performance-analyst.md @@ -0,0 +1,13 @@ +--- +name: performance-analyst +description: Reviews account performance, audiences, attribution, and measurement gaps; recommends next actions without executing changes. +model: opus +effort: high +disallowedTools: Write, Edit +--- + +Confirm the workspace and account IDs with `list_connected_accounts`. Pull comparable periods with `pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, and `pull_reddit_ads_performance` where connected. + +Use `ga4_get_report`, `get_attribution`, and `get_spend_reconciliation` to explain differences between platform claims and analytics. Review `list_audiences` and `get_pixel_health` for available audience and tracking evidence. Request platform exports for missing breakdowns or change history. Never present a correlation as a proven cause. + +Lead with spend, conversions, CPA/ROAS, and the comparison period. Explain the supported causes, measurement limits, and practical next actions. Name the platform, account, and account ID for every recommendation. Hand proposed changes to the budget-optimizer or media-buyer for explicit user approval; do not execute them. diff --git a/packages/claude-directory/assets/logo.png b/packages/claude-directory/assets/logo.png new file mode 100644 index 0000000..42ca405 Binary files /dev/null and b/packages/claude-directory/assets/logo.png differ diff --git a/packages/claude-directory/context/brand-and-safety.md b/packages/claude-directory/context/brand-and-safety.md new file mode 100644 index 0000000..fdcfee4 --- /dev/null +++ b/packages/claude-directory/context/brand-and-safety.md @@ -0,0 +1,33 @@ +# Synter — Brand Voice & Safety (read before generating copy or spending) + +## Advertiser identity +Before writing copy or selecting assets, confirm the workspace ID, advertiser name, and domain using account evidence and the user’s verified context. Obtain the advertiser’s brand kit, voice, and original assets from the user or existing workspace assets returned by `list_creative_assets`. Use that context explicitly in supported tool fields. Do not apply Synter’s own voice or branding to another advertiser. Ask for missing brand details; never invent them. + +## Synter voice (only when Synter is the verified advertiser) +Sound like the agent talking: terse, certain, doing the work. Show, don't sell. +- Lead with the action or the result. ("Found $3k in wasted spend.") +- Short declaratives, 5–12 words. One idea per sentence. +- No hedging, no justification. State it. +- Concrete over abstract — real numbers, real platform names, real outcomes. +- First person, present tense, as the agent. ("Pulling your spend." "Nothing ships without your approval.") +- Confidence without volume. No exclamation points, no all-caps, no emojis. + +## Forbidden language — never use +- **"AI-Powered"** / "AI-driven" / "powered by AI" → use "Autonomous AI Execution", "AI Agents", "AI Agents that execute". +- "Revolutionary", "disruptive", "game-changing", "cutting-edge", "next-gen", "best-in-class". +- "Leverage", "utilize", "optimize", "streamline", "empower", "enable", "unlock". +- "Seamless", "frictionless", "effortless", "10x", "supercharge", "turbocharge". +- "Act now!"-style hype CTAs. +- **"Beta"** in any form — there is no beta. Synter is a live product. + +## Accuracy +Never fabricate a price, competitor, statistic, or claim. If you don't have the real number, say so. A plain true line beats an invented impressive one. + +## Safety — money rules +- Nothing that spends money ships without explicit user approval. No exceptions. +- Default to recommend-then-execute: propose the plan (platform, budget, audience, creative), show it, and wait for an explicit yes before any create/enable/budget/launch/pause call. +- Read-only reports and account lookups do not require approval. +- Confirm the org/account before any write (`list_connected_accounts`). Never run one org's campaigns through another's credentials or session. +- Use real account/campaign IDs the tools return — never invent them. +- Guard budgets against fat-finger amounts (a daily budget 10–100x intended is the classic incident); default to sane ceilings. +- "Created" ≠ "live and spending." Verify before claiming a campaign is running. diff --git a/packages/claude-directory/output-styles/synter.md b/packages/claude-directory/output-styles/synter.md new file mode 100644 index 0000000..48e4bbd --- /dev/null +++ b/packages/claude-directory/output-styles/synter.md @@ -0,0 +1,16 @@ +--- +name: synter +description: Synter's agent voice — terse, certain, doing the work. Lead with the result, real numbers, no hype. +--- + +Respond like the Synter operator: terse and certain, doing the work right now. + +- Lead with the action or the result. No throat-clearing. +- Short declaratives. One idea per sentence. Expand only when the reader needs detail. +- Concrete over abstract — real numbers, real platform names, real outcomes. +- First person, present tense, as the agent ("Pulling your spend.", "Executing end-to-end."). +- Recommend-then-execute: propose the plan (platform, budget, audience, creative), show it, and wait for an explicit yes before any create/enable/budget/launch/pause call. Read-only reports and account lookups run without asking. +- Confidence without volume: no exclamation points, no all-caps, no emojis, no hype. +- Never use banned language ("AI-Powered", "seamless", "revolutionary", "leverage", "beta", "Act now!"). Use approved verbs: Operate, Ship, Platforms, Direct API, AI Agents. +- Never fabricate a price, competitor, or statistic. If you don't have the real number, say so. +- Before reporting a campaign live or a change done, verify it. "Created" is not "live and spending." diff --git a/packages/claude-directory/skills/ad-copy-generation/SKILL.md b/packages/claude-directory/skills/ad-copy-generation/SKILL.md new file mode 100644 index 0000000..ce6157b --- /dev/null +++ b/packages/claude-directory/skills/ad-copy-generation/SKILL.md @@ -0,0 +1,37 @@ +--- +name: ad-copy-generation +description: Write ad copy that fits the platform and passes policy — Google RSA headlines and descriptions, social primary text and hooks, and variations for testing. Use when a user needs ad copy, headlines, descriptions, RSA assets, or fresh variants for underperforming ads. +--- + +# Ad Copy Generation + +Write copy that matches the platform's format, the searcher's intent, and the brand's voice. This is a creation task; it does not spend money. + +Apply brand voice from `../../context/brand-and-safety.md` to everything you write. + +## Fit the format + +- **Search (Google, Microsoft) RSAs:** meet the platform’s required counts and character limits. Prefer fewer concise, distinct headlines that match search intent, state the offer, and deliberately qualify the intended audience. Use the ad group’s actual keywords where they read naturally. Never pad to 15 headlines or impose an arbitrary 11-headline floor. Ad Strength is not a launch gate or a reason to add filler. Pin only when a legal or brand line must always appear. Rewrite strong lines to fit instead of truncating them. +- **Social (Meta, TikTok, Reddit, X):** primary text plus a hook that earns the first second. Match the platform's native tone; a LinkedIn line is not a TikTok line. +- **LinkedIn:** professional, outcome-led, decision-maker framing. + +## Write for intent + +- Speak to what the user wants at that moment, not what the product is. +- One idea per line. Concrete over abstract; real numbers over adjectives. +- Include a clear next step using an approved CTA. + +## Generate and vary + +Use `create_ad_copy` for volume, then edit for voice and accuracy. Produce enough distinct variants to test one variable at a time (see **creative-testing**). Never fabricate a price, claim, or statistic; if the real number is unknown, leave it out. + +## Check before it ships + +Run every variant through the **ad-policy-compliance** thinking and the brand forbidden-word list. Confirm claims are true and the final URL matches the promise. + +## Rules + +- On brand and on policy, every line. No forbidden words, no invented claims. +- RSA assets must be relevant, accurate, and within platform limits. Use intentional disqualification where it fits the advertiser’s audience; do not fill optional slots just to increase a score. No filler or truncation. +- RSAs get varied, unpinned assets unless a line must always appear. +- Report the variants and the one variable each set is built to test. diff --git a/packages/claude-directory/skills/ad-policy-compliance/SKILL.md b/packages/claude-directory/skills/ad-policy-compliance/SKILL.md new file mode 100644 index 0000000..f1e6f2b --- /dev/null +++ b/packages/claude-directory/skills/ad-policy-compliance/SKILL.md @@ -0,0 +1,574 @@ +--- +name: ad-policy-compliance +description: Checks ad creative and landing pages against platform advertising policies for Google, Meta, LinkedIn, TikTok, and Reddit. Use when pre-checking ads for policy compliance, fixing rejected ads, reviewing restricted content, or navigating industry-specific ad rules; verify sensitive categories against live platform policy before launch. +--- + +# Ad Policy Compliance + +Pre-checks ad creative, copy, and landing pages against advertising policies for Google Ads, Meta Ads, LinkedIn Ads, TikTok Ads, and Reddit Ads. Covers restricted content categories, trademark rules, industry-specific requirements, Special Ad Categories, and common rejection reasons with fixes. + +## Capabilities + +- **Google Ads Policy Scan**: Restricted content, editorial, trademark, claims +- **Meta Ads Policy Scan**: Special Ad Categories, prohibited content, targeting restrictions +- **LinkedIn Ads Policy Checklist**: Professional standards, B2B compliance +- **TikTok Ads Policy Check**: Authenticity, community guidelines, restricted products +- **Reddit Ads Policy Check**: Community standards, transparency rules +- **Industry-Specific Rules**: Finance, healthcare, alcohol, political, gambling, cannabis +- **Pre-Submission Workflow**: Automated policy scan before ad creation +- **Rejection Fix Playbook**: Common rejections and proven solutions + +## Workflows + +### 1. Pre-Submission Policy Scan + +**Run this checklist before submitting ANY ad on ANY platform:** + +``` +UNIVERSAL CHECKS (all platforms): + +✅ COPY CHECKS: + □ No excessive capitalization (≤1 ALL CAPS word per headline) + □ No excessive punctuation (no "!!!" or "???") + □ No misleading claims ("guaranteed results", "#1 product") + □ No before/after images implying unrealistic results + □ No fake UI elements (fake buttons, fake notifications) + □ No countdown timers with false urgency + □ Grammar and spelling correct + □ CTA matches landing page action + +✅ TARGETING CHECKS: + □ No targeting by race, ethnicity, or religion (any platform) + □ Age-appropriate targeting for product type + □ No targeting children (under 13 — COPPA) + □ Geo-targeting matches product availability + □ Special Ad Category applied if required (Meta) + +✅ LANDING PAGE CHECKS: + □ Page loads and is functional + □ Content matches ad claims + □ Privacy policy accessible + □ No auto-downloads or malware + □ No deceptive navigation (fake X buttons, fake content) + □ Contact information visible (required for some industries) + □ No pop-up blocks preventing page exit + +✅ LEGAL CHECKS: + □ Required disclaimers present (financial, health, political) + □ Trademark usage authorized OR generic terms used + □ "Ad" disclosure on native/influencer content + □ Terms and conditions linked for offers + □ Age gates for restricted products (alcohol, gambling) +``` + +### 2. Google Ads Policy Checklist + +**Editorial policies (most common rejections):** + +| Violation | Example | Fix | +|-----------|---------|-----| +| Excessive capitalization | "FAST SHIPPING on ALL ORDERS" | "Fast Shipping on All Orders" | +| Excessive punctuation | "Amazing Deal!!!" | "Amazing Deal" | +| Gimmicky use of symbols | "★★★ Best CRM ★★★" | Remove symbols from ad text | +| Misleading ad | "Click Here ↓↓↓" | Use descriptive CTA | +| Non-standard spacing | "S A L E" | "Sale" | +| Phone number in ad text | "Call 1-800-555-1234" | Use call extension instead | +| Unclear relevance | Ad about shoes → LP about insurance | Match ad to LP content | + +**Restricted content:** + +``` +RESTRICTED (allowed with limitations): + □ Alcohol: Allowed in some countries, must target 21+ (US) + □ Gambling: Requires Google certification, country-specific + □ Healthcare/pharma: OTC drugs OK, prescription requires cert + □ Financial services: Must comply with local regulations + □ Political ads: Requires identity verification, "Paid for by" disclosure + □ Adult content: Allowed with restrictions (no explicit imagery) + □ Trademarks: Allowed in ad text if you're authorized reseller/informational + +PROHIBITED (never allowed): + ❌ Counterfeit goods + ❌ Dangerous products (explosives, recreational drugs) + ❌ Dishonest behavior (phishing, malware, clickbait) + ❌ Inappropriate content (hate speech, violence, bullying) + ❌ Weapons and weapon accessories + ❌ Tobacco and tobacco products + ❌ Bail bonds services (US) + ❌ Surveillance equipment +``` + +**Trademark policies:** + +``` +WHEN TRADEMARKS ARE ALLOWED IN GOOGLE ADS: + 1. You are an authorized reseller of the trademarked product + 2. You are providing informational content about the product + 3. You are comparing your product to the trademarked product + +HOW TO USE: + - You CAN bid on competitor brand keywords (always) + - You CAN mention competitors in ad text IF: + a) You're comparing products accurately + b) You link to a comparison/informational page + c) You don't imply affiliation + - You CANNOT use trademark in display URL + - You CANNOT use competitor logos in display ads + +IF YOUR AD IS REJECTED FOR TRADEMARK: + 1. Request authorization from trademark owner + 2. OR submit a trademark authorization form to Google + 3. OR remove the trademark from ad text (keep bidding on keyword) + 4. Appeals: ads.google.com/nav/selectaccount → Tools → Trademark +``` + +**Google Ads claim restrictions:** + +``` +CLAIMS THAT TRIGGER REVIEW: + "Best [product]" → Requires third-party verification + "Guaranteed results" → Must have refund/guarantee policy on LP + "#1 [product/service]" → Requires verifiable ranking source + "FDA approved" → Must actually be FDA approved + "As seen on [media]" → Must have been featured + "Doctor recommended" → Requires verifiable medical endorsement + "[X]% success rate" → Must cite source and methodology + +SAFE ALTERNATIVES: + "Best" → "Top-rated" or "Award-winning" (with award cited) + "Guaranteed" → a specific, substantiated description of the offer + "#1" → "Leading" or "Trusted by [X,000]+" + "Instant results" → "See results in as little as [timeframe]" +``` + +### 3. Meta Ads Policy Checklist + +**Special Ad Categories (MANDATORY for these topics):** + +``` +HOUSING: + Applies to: Real estate listings, mortgages, home insurance, apartments + Restrictions: + ❌ Cannot target by ZIP code (must use 15-mile radius minimum) + ❌ Cannot target by age, gender, or parental status + ❌ Cannot use lookalike audiences + ❌ Cannot exclude interests related to protected classes + +EMPLOYMENT: + Applies to: Job listings, hiring, employment opportunities + Restrictions: Same as Housing + +CREDIT: + Applies to: Credit cards, loans, mortgages/financing, BNPL and other credit products + Restrictions: Same as Housing + +SOCIAL ISSUES / ELECTIONS / POLITICS: + Applies to: Political candidates, ballot measures, social advocacy + Requirements: + ✅ Must have "Paid for by" disclaimer + ✅ Must verify identity and location + ✅ Must be authorized to run political ads + ✅ Ads stored in Meta Ad Library for 7 years + ✅ Must disclose qualifying photorealistic/video/audio AI-generated or digitally altered content + ⚠️ Ads about social issues, elections or politics are not allowed to run in the EU; + rules and designated-country authorization requirements vary by region +``` + +**Prohibited content (Meta-specific):** + +``` +❌ PROHIBITED: + - Tobacco products or paraphernalia + - Unsafe supplements (weight loss, body building — many banned) + - Weapons, ammunition, or explosives + - Adult products or services + - Multi-level marketing (income claims) + - Payday loans or predatory financial products + - Penny auctions + - Counterfeit documents + - Spyware or malware + - Discriminatory practices + +⚠️ RESTRICTED (requires approval): + - Alcohol (21+ targeting in US, varies by country) + - Dating services (must not promote casual encounters) + - Online gambling (license required, geo-restricted) + - Cryptocurrency and related products/services (prior written permission; product and country restrictions) + - Prescription drugs (must target only authorized countries) + - Financial products (disclosures/licensing as required by product and market) +``` + +**Meta-specific creative rules:** + +``` +TEXT IN IMAGES: + - No longer a hard 20% text rule (removed 2021) + - BUT: Less text = more delivery (algorithm preference) + - Recommendation: Keep text minimal in ad images + +BEFORE/AFTER IMAGES: + ❌ Cannot imply unrealistic results + ❌ Cannot use before/after for weight loss + ✅ CAN use for home renovation, cleaning products (non-body) + +PERSONAL ATTRIBUTES: + ❌ Cannot assert or imply personal attributes + ❌ "Are you overweight?" → REJECTED + ❌ "Struggling with debt?" → REJECTED + ✅ "Many people manage their weight with..." → APPROVED + ✅ "Tools to help manage finances" → APPROVED + + Rule: Don't use "you" + negative attribute. + Use third-person or positive framing instead. + +FACEBOOK BRAND REFERENCES: + ✅ "Follow us on Facebook" (lowercase 'facebook' in running text is OK) + ❌ "Join our FACEBOOK group" (cannot emphasize) + ❌ Modified Facebook logos or trademark + ✅ Official Facebook brand assets (from brand.facebook.com) +``` + +### 4. LinkedIn Ads Policy Checklist + +**Professional standards:** + +``` +REQUIRED: + ✅ Professional tone (no clickbait, no sensationalism) + ✅ Accurate job titles and company claims + ✅ Clear identification of advertiser + ✅ Appropriate for professional audience + ✅ Truthful company/product descriptions + +PROHIBITED: + ❌ Profanity or vulgar language + ❌ Sexually suggestive content + ❌ Misleading credentials or qualifications + ❌ Fake urgency ("Only 3 spots left!" without truth) + ❌ "Get rich quick" schemes or unrealistic income claims + ❌ Deceptive subscription practices + ❌ Gambling or betting services + ❌ Weapons and military equipment +``` + +**LinkedIn-specific restrictions:** + +``` +LEAD GEN FORMS: + ✅ Must include privacy policy URL + ✅ Must clearly state what user is signing up for + ✅ Must not pre-check consent boxes + ❌ Cannot request SSN, government ID, or financial account numbers + ❌ Cannot request health or medical information + ⚠️ Custom questions must comply with data collection policies + +SPONSORED INMAIL / MESSAGE ADS: + ✅ Must clearly identify sender + ✅ Subject line must match content + ✅ Must comply with LinkedIn message-ad format requirements and applicable consent/privacy laws + ⚠️ Frequency caps and product availability change; verify in Campaign Manager/live docs +``` + +### 5. TikTok Ads Policy Checklist + +**Authenticity requirements:** + +``` +REQUIRED: + ✅ Ads must be clearly identifiable as advertising + ✅ Must not mimic TikTok UI elements (fake like buttons, etc.) + ✅ Landing page must be functional and match ad claims + ✅ Product availability must match targeted regions + ✅ Accurate offer terms (no omitted conditions) + +CREATIVE RULES: + ✅ Sound is expected (unlike other platforms) — include audio + ✅ Vertical format preferred (9:16) + ✅ Native-feeling content performs best (less polished = better) + ❌ No fake engagement metrics ("liked by X people") + ❌ No fake system notifications + ❌ No countdowns without real deadlines +``` + +**TikTok-specific restrictions:** + +``` +PROHIBITED: + ❌ Weight loss surgery/procedures and harmful body-image or high-risk weight-management claims; + other weight-management products are market-specific and often restricted/prohibited + ❌ Tobacco, e-cigarettes, and vaping products + ❌ Weapons and weapon accessories + ❌ Political ads (global ban — TikTok doesn't allow political advertising) + ❌ ICOs, token sales, payday loans, P2P lending, get-rich-quick and MLM/pyramid schemes + ❌ Payday loans + ❌ Adult content or dating for minors + +RESTRICTED: + ⚠️ Alcohol: Allowed in select markets with age targeting (21+/18+) + ⚠️ Gambling: Licensed operators in approved markets only + ⚠️ Financial services: Market-specific licensing/disclosures; generally 18+; some require TikTok Sales approval + ⚠️ Crypto exchanges/custodial wallets/NFTs: Only in approved markets with required licenses, + TikTok permission/application, and 18+ targeting; never frame NFTs as investments/guaranteed returns + ⚠️ Health supplements/OTC/medical devices: Must not make medical claims or prohibited before/after comparisons + ⚠️ Beauty/skincare: No unrealistic transformation claims +``` + +**AI-generated and branded/commercial disclosure:** + +``` + ✅ Use TikTok's commercial/branded content disclosure toggles for paid partnerships or promotional Spark Ads + ✅ Label realistic AI-generated or significantly altered media where TikTok requires it + ❌ Do not use AI-generated likenesses of real people for political or commercial endorsements + ⚠️ TikTok Ads and TikTok Branded Content policies are separate; check both for creator/Spark campaigns +``` + +### 6. Reddit Ads Policy Checklist + +**Restricted/prohibited categories:** + +``` +PROHIBITED / HIGH-RISK: + ❌ Tobacco, weapons, counterfeit goods, recreational drugs, adult sexual content, malware/scams + ❌ Payday/predatory lending, single securities/tokens/coins, ICOs/IDOs/token sales, + liquidity pools, mixers/tumblers, trading bots, unlicensed bank-like services + +RESTRICTED: + ⚠️ Alcohol: Must comply with local law and age-gating (21+ in US); no excessive consumption + ⚠️ Gambling/gaming: Licensed, legal markets only; direct Reddit Sales management may be required + ⚠️ Financial/crypto: Requires vetting; many products require direct Reddit Sales management and documentation + ⚠️ Healthcare: Claims must be substantiated; comply with Reddit healthcare policy and local law +``` + +**Political ads (Reddit):** + +``` +US: + ✅ Permitted only for US federal-level political ads with Reddit certification and Sales representative management + ✅ Candidate/official representative must participate in a Reddit AMA before placement + ✅ Must include clear "paid for by" disclosure and link/comment to the AMA + ❌ Cannot target by ZIP code or gender + ❌ Attack ads and misleading deepfakes/manipulated content are prohibited + ✅ Synthetic content that is otherwise allowed must include a clear disclosure (e.g., "AI-generated content") + +EU: + ⚠️ Requires direct Reddit Sales management and acknowledgement that ads are not designed to influence + election/referendum outcomes, voting behavior, or legislative/regulatory processes +``` + +### 7. Industry-Specific Rules + +#### Financial Services + +``` +ALL PLATFORMS: + ✅ Include APR, fees, and terms in landing page + ✅ "Terms and conditions apply" disclaimer + ✅ Licensed/registered financial institution disclosure + ✅ Risk warnings for investment products + ✅ "Past performance is not indicative of future results" + + ❌ No guaranteed returns claims + ❌ No income claims without proof + ❌ No targeting minors + +GOOGLE ADS SPECIFIC: + - Must comply with local financial regulations + - Requires Google advertiser or product certification/approval for some financial products and markets + - US personal loans: APR 36%+ not allowed; loan ads must include required cost/term disclosures + - Crypto: Certain products may be allowed with or without certification in limited circumstances; + exchanges, wallets and coin trusts generally require Google certification and local compliance + - Credit repair ads are not allowed; debt services are allowed only in eligible locations with approval + +META SPECIFIC: + - Falls under CREDIT Special Ad Category (if credit-related) + - Cannot target by ZIP code, age, or gender for credit products + - Must include disclaimers in ad or landing page + - Crypto: Requires Meta prior written permission; eligibility depends on product, country, + authorization documentation and current Meta crypto policy +``` + +#### Healthcare & Pharmaceuticals + +``` +ALL PLATFORMS: + ✅ Must not make unsubstantiated health claims + ✅ Must comply with HIPAA (US) / GDPR (EU) for data collection + ✅ OTC products: Generally allowed with disclaimer + ✅ Telehealth: Allowed with proper licensing disclosure + + ❌ Prescription drug promotion is platform-, country-, advertiser-authorization-, and audience-specific; + do not assume DTC prescription ads are allowed without live policy/legal verification + ❌ No miracle cure claims + ❌ No before/after weight loss imagery + ❌ No patient testimonials implying guaranteed outcomes + +GOOGLE ADS SPECIFIC: + - Prescription drugs: Only certified pharmacies (LegitScript cert) + - Addiction treatment: Requires LegitScript certification + - Clinical trials: Requires Google approval + - COVID-related: Enhanced review, fact-checking required + +META SPECIFIC: + - Cannot target by health condition ("diabetes" interest OK for awareness, + but not for selling diabetes products) + - Supplements: Must not make drug claims + - Mental health: Allowed but no personal attribute assertions + +TIKTOK SPECIFIC: + - Weight-management, aesthetic treatment, healthcare and pharmaceutical rules are highly market-specific + - No medical claims for supplements/beauty products unless the market policy allows and licensing is approved + - Prescription medication/pharmacies generally require market-specific permission/certification or are prohibited +``` + +#### Alcohol + +``` +US RULES: + ✅ Must target 21+ (LDA — Legal Drinking Age) + ✅ Must not target pregnant women + ✅ Must not show excessive consumption + ✅ Must not associate drinking with driving + ✅ Must include "Drink Responsibly" or similar + +PLATFORM STATUS: + Google Ads: ✅ Allowed with restrictions (21+ targeting, no promotion of excessive drinking) + Meta: ✅ Allowed with age-gating (varies by country) + LinkedIn: ⚠️ Restricted; available only in certain countries and generally requires working with LinkedIn Ad Sales + TikTok: ⚠️ Market-specific; often prohibited for branded content and restricted/approval-gated for paid ads + Reddit: ✅ Allowed with age-gating + +INTERNATIONAL: + France: No alcohol advertising allowed (Loi Évin) + Norway/Sweden/Finland: Heavily restricted + UK: Allowed with ASA guidelines (no appeal to under-18s) + India: Prohibited on all platforms + Middle East: Prohibited (most countries) +``` + +#### Political Advertising + +``` +GOOGLE ADS: + ✅ Election ads require region-specific verification where Google requires it + ✅ "Paid for by" disclosure required for verified election advertisers + ✅ Stored in Google Ads Transparency Center where transparency reporting applies + ✅ Must comply with local election laws + ✅ Synthetic/digitally altered election content must be disclosed in campaign settings; + some formats also need prominent in-ad disclosure + ⚠️ Targeting restrictions and regional prohibitions vary; verify live policy for the target country + +META: + ✅ Requires "Paid for by" disclaimer + ✅ Must verify identity and location + ✅ Stored in Meta Ad Library for 7 years + ✅ Special Ad Category: Social Issues / Elections / Politics + ✅ Must disclose qualifying AI-generated/digitally altered realistic media + ❌ Not allowed to run in the EU; elsewhere targeting/authorization rules vary by country + +LINKEDIN: + ❌ Political advertising not accepted + +TIKTOK: + ❌ Political advertising globally banned (since 2019) + +REDDIT: + ⚠️ US federal-level political ads only, with Reddit certification, direct Sales management, + candidate/representative AMA prerequisite, "paid for by" disclosure, and public transparency + ❌ No ZIP-code or gender targeting; attack ads and misleading deepfakes are prohibited + ✅ Synthetic political ad content that is otherwise allowed must be clearly disclosed + +X (TWITTER): + ❌ Political advertising banned (since 2019, reversed 2023) + ⚠️ Allowed again with restrictions — requires certification +``` + +### 8. Common Rejection Reasons and Fixes + +| Rejection | Platform | Cause | Fix | +|-----------|----------|-------|-----| +| "Misleading content" | Google | Exaggerated claims, clickbait | Remove superlatives, add qualifiers | +| "Circumventing systems" | Google | Redirects, cloaking | Use direct URLs, no redirect chains | +| "Unacceptable business model" | Google | MLM, get-rich-quick | Restructure LP to remove income claims | +| "Personal attributes" | Meta | "Are you struggling with..." | Use "Many people find..." (third person) | +| "Non-functional LP" | All | 404, slow load, broken forms | Fix page, test all links | +| "Text in image" | Meta | Too much text overlay | Reduce text, use less intrusive supers | +| "Special Ad Category" | Meta | Housing/credit/employment content without SAC | Apply appropriate category | +| "Trademark violation" | Google | Using competitor brand in ad text | Remove trademark or get authorization | +| "Restricted content" | All | Alcohol, gambling without age gate | Add age targeting, required disclaimers | +| "Destination mismatch" | Google | Ad about X, LP about Y | Align LP content with ad copy | +| "Malware/unwanted software" | Google | Sketchy LP, auto-downloads | Remove any downloads, fix SSL | +| "Low quality" | LinkedIn | Clickbait, unprofessional | Professional tone, clear value prop | + +### 9. Trademark Usage Rules by Platform + +| Rule | Google | Meta | LinkedIn | TikTok | Reddit | +|------|--------|------|----------|--------|--------| +| Bid on competitor keywords | ✅ Always | ✅ Always | ✅ Always | ✅ Always | ✅ Always | +| Use trademark in headline | ⚠️ Need auth | ✅ Comparison OK | ⚠️ Need auth | ⚠️ Need auth | ✅ If accurate | +| Use trademark in description | ⚠️ Need auth | ✅ Comparison OK | ⚠️ Need auth | ⚠️ Need auth | ✅ If accurate | +| Use trademark in display URL | ❌ Never | N/A | N/A | N/A | N/A | +| Use competitor logo in creative | ❌ Never | ❌ Never | ❌ Never | ❌ Never | ❌ Never | +| Comparison claims | ✅ If truthful | ✅ If truthful | ✅ If truthful | ✅ If truthful | ✅ If truthful | + +## Reference Data + +### Appeal Process by Platform + +| Platform | Appeal Method | Response Time | Success Rate | +|----------|---------------|---------------|--------------| +| Google Ads | Policy Manager → Request Review | 1-3 business days | 30-50% | +| Meta | Ad rejected → Request Review button | 1-5 business days | 20-40% | +| LinkedIn | Campaign Manager → Contact Support | 3-5 business days | 40-60% | +| TikTok | Ad rejected → Appeal button | 1-3 business days | 25-45% | +| Reddit | Email support@reddithelp.com | 3-7 business days | 30-50% | + +### Pre-Launch Compliance Checklist Template + +```markdown +# Ad Compliance Pre-Launch Checklist + +**Campaign:** [Name] +**Platform:** [Google / Meta / LinkedIn / TikTok / Reddit] +**Industry:** [Standard / Financial / Healthcare / Alcohol / Political] +**Date:** [Date] +**Reviewer:** [Name] + +## Universal Checks +- [ ] No misleading claims or exaggerated promises +- [ ] No excessive caps or punctuation +- [ ] CTA matches landing page action +- [ ] Landing page functional (tested on mobile + desktop) +- [ ] Privacy policy accessible on landing page +- [ ] No prohibited content (weapons, drugs, tobacco, hate) +- [ ] No fake UI elements or deceptive design + +## Platform-Specific Checks +- [ ] Special Ad Category applied if needed (Meta) +- [ ] No personal attribute assertions (Meta) +- [ ] Professional tone (LinkedIn) +- [ ] Political ad disclosures if applicable +- [ ] Trademark usage authorized +- [ ] Age targeting set for restricted products + +## Industry-Specific Checks +- [ ] Required disclaimers present +- [ ] Licensing/certification displayed +- [ ] Risk warnings included (financial) +- [ ] "Drink Responsibly" included (alcohol) + +## Result +- [ ] ✅ APPROVED for submission +- [ ] ❌ BLOCKED — issues found (list below) + +**Issues found:** +1. [Issue description + fix required] +2. [Issue description + fix required] +``` + +## Examples + +### Example: Campaign policy review + +Record the advertiser, platform, final ad text, destination, and evidence supporting each claim. Flag unsupported promises or missing offer conditions. Ask the advertiser for verified terms instead of filling in examples that could be mistaken for an actual offer. + +Return the checks passed, the unresolved issues, and the changes required before submission. A policy review does not guarantee platform acceptance or authorize deployment. diff --git a/packages/claude-directory/skills/anomaly-detector/SKILL.md b/packages/claude-directory/skills/anomaly-detector/SKILL.md new file mode 100644 index 0000000..e931df3 --- /dev/null +++ b/packages/claude-directory/skills/anomaly-detector/SKILL.md @@ -0,0 +1,36 @@ +--- +name: anomaly-detector +description: Catch spend spikes, CTR or conversion-rate drops, budget runaway, and metric outliers across campaigns, and set sane alert thresholds. Use when a user asks why spend or performance changed suddenly, wants monitoring or alerts, or something looks off in the numbers. +--- + +# Anomaly Detector + +Find the sudden change before it burns a budget. Reads do not require approval; setting alerts or pausing is a change and waits for approval. + +Confirm the account: `list_connected_accounts`. + +## 1. Pull a baseline and today + +Read a stable historical window and the recent period with `pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, or `pull_reddit_ads_performance`. State the period and missing data before calling a change abnormal. + +## 2. Find real outliers, not noise + +- **Spend spike / budget runaway:** daily spend jumping well above the recent average, or pacing to exhaust the budget far too early. This is the one to catch fastest. +- **CTR or conversion-rate drop:** a sharp fall against the rolling average signals a broken creative, landing page, or tracking tag, not just a bad day. +- **CPA / CPM jump:** rising cost per result or per impression points to auction pressure, audience saturation, or fatigue. +- Judge against variation, not a single day. A metric one bad day inside normal swing is not an anomaly; a sustained multi-day move outside the usual range is. State the window and the size of the move. + +## 3. Diagnose before you alarm + +Rule out the mundane first: a tracking outage reads as a conversion cliff; a new campaign launch reads as a spend spike; a weekend reads as a volume drop. Confirm the cause before recommending action. + +## 4. Act + +- Recommend alert thresholds for the user to configure in the platform UI. Do not claim alerts were installed. +- For a confirmed problem, recommend the fix (pause, budget cut, creative swap) and take it through the relevant skill on approval. Do not pause on one noisy data point; see **pre-pause-analysis**. + +## Rules + +- Distinguish signal from noise; every flag names the window and the magnitude. +- Rule out tracking, launches, and seasonality before calling a metric broken. +- Report anomalies ranked by budget impact, with the recommended action and who approves it. diff --git a/packages/claude-directory/skills/attribution/SKILL.md b/packages/claude-directory/skills/attribution/SKILL.md new file mode 100644 index 0000000..6ddee1f --- /dev/null +++ b/packages/claude-directory/skills/attribution/SKILL.md @@ -0,0 +1,30 @@ +--- +name: attribution +description: Measure which platforms and touches actually drive conversions across channels, compare attribution views, and read assisted conversions and conversion paths. Use when a user asks what is really working, wants cross-platform attribution, or questions a single platform's self-reported numbers. +--- + +# Attribution + +Every platform claims the conversion. Attribution is how you see the truth across them and spend where it actually works. Reads only; this is measurement, not a spend change. + +Confirm the account: `list_connected_accounts`. + +## 1. Get the cross-platform view + +Use `get_attribution` for the cross-platform attribution view and `get_spend_reconciliation` for spend differences. Compare conversion counts from `ga4_get_report` with `pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, and `pull_reddit_ads_performance` for connected accounts. State what each source measures before interpreting differences. + +## 2. Read the discrepancy honestly + +- Platforms over-claim on their own last-click view; summed platform conversions usually exceed real total conversions. +- Look at assisted conversions and conversion paths, not just last click. A platform that rarely closes but frequently assists still earns budget. +- View-through and different attribution windows inflate some platforms more than others. Name the window when you compare. + +## 3. Turn it into a decision + +Reallocate toward channels that drive incremental conversions, not the ones best at claiming credit. Where the stakes justify it, the true test is a holdout, not a model; be clear that attribution models estimate and only an incrementality test proves lift. + +## Rules + +- Never take one platform's self-reported ROAS as the whole truth. Reconcile first. +- State the attribution window and model behind any number you give. +- Report where platforms disagree and what you would move, then hand budget moves to the **optimize** skill for approval. diff --git a/packages/claude-directory/skills/audience-expansion-strategy/SKILL.md b/packages/claude-directory/skills/audience-expansion-strategy/SKILL.md new file mode 100644 index 0000000..b56e001 --- /dev/null +++ b/packages/claude-directory/skills/audience-expansion-strategy/SKILL.md @@ -0,0 +1,60 @@ +--- +name: audience-expansion-strategy +description: Expand targeting without wrecking CPA — lookalike ladders, seed quality, audience layering, and exclusion hygiene across platforms. Use when a user wants to scale beyond exhausted audiences, build lookalikes, fix audience fatigue, or set up exclusion lists. +--- + +# Audience Expansion Strategy + +Expansion is a ladder, not a leap: each rung trades a little efficiency for a lot of reach, and you climb only when the rung below is saturated. + +Confirm the account and inventory what exists: `list_connected_accounts`, `list_audiences`, and performance per audience via the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`). + +## 1. Audit before expanding + +Find which audiences drive 80% of conversions, which run above target CPA, and which show fatigue: frequency climbing past ~3-4/week, CTR down 20%+ week over week, CPA up 25%+ week over week. Expansion fixes saturation; it does not fix a broken offer or tired creative (see **ad-copy-generation**). + +## 2. Climb the expansion ladder + +- **Tier 1, hottest:** retargeting pools and 1% lookalikes of customers. Expect ~0.5-0.8x average CPA. +- **Tier 2, warm:** 1-3% lookalikes from high-value customers, synced prospect lists, engaged audiences. ~0.8-1.2x. +- **Tier 3, cool:** 3-5% lookalikes, in-market and interest targeting, competitor-interest audiences. ~1.2-1.8x. +- **Tier 4, cold:** 5-10% lookalikes, broad/algorithmic targeting. ~1.5-2.5x. + +Add one tier at a time with its own budget and CPA ceiling; kill any rung that exceeds its ceiling with volume behind it (see **kill-scale-rules**). + +## 3. Build lookalikes from quality seeds + +Seed quality decides lookalike quality. Ranked: top-20%-by-LTV customers, then all converters, then intent signals (cart, demo, offer page), then engagement signals. Practical minimums for good results are ~1,000 matched users on most platforms regardless of lower stated floors. Two improvements that consistently cut lookalike CPA: seed from top-value customers instead of all customers, and seed from recent (90-day) converters instead of all-time. Refresh seeds monthly. + +Review existing seeds with `list_audiences`. Ask the account owner to create any required expansion audiences in the platform UI, then compare them in a controlled test. Use returned audience IDs in supported campaign-plan fields; follow **launch** for approval. + +## 4. Layer for precision, exclude for hygiene + +Describe the intended account criteria, exclusions, and layering. Use `research_campaign_opportunity` for available campaign research; hand off audience construction to the platform UI. + +Mandatory exclusions on every prospecting campaign: + +- Existing customers and recent converters; ask the account owner to refresh suppression lists weekly. +- All retargeting pools — prospecting money should buy strangers. +- Employees and CRM-disqualified leads. + +Sequential exclusion prevents one user being hit at every funnel stage at once: awareness excludes considerers, consideration excludes converters, conversion excludes purchasers. + +When uploading lists, format for match rate: lowercase and trim emails, E.164 phone numbers with country code, first and last name in separate fields. Hashed email plus phone typically matches 55-80% of records on Meta and Google; email alone matches meaningfully less. A list that matches under 30% is a data-quality problem, not a platform problem. + +## 5. Decide when to hand targeting to the algorithm + +Meta Advantage+ audiences and Google Optimized Targeting treat your audiences as suggestions and expand beyond them. Turn them on with strong conversion volume (50+ conversions/week on Meta, 30+/month on Google) and real budget; keep them off for niche B2B ICPs, thin pixels, or when testing a specific audience hypothesis. Exclusions are respected either way — which is another reason the suppression lists must be current. + +## 6. Monitor and rebalance + +Weekly: the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`) per audience tier. Scale rungs beating target, refresh creative on fatiguing ones, and rebalance spend across tiers with `optimize_budget`. When an expansion audience wins, feed its converters back into the seed pool — the ladder compounds. + +## Rules + +- One new tier at a time, each with a pre-set CPA ceiling. Expansion without a ceiling is just spray. +- Never expand to escape creative fatigue; rotate creative first (see **creative-testing**). +- Suppression lists refresh weekly, everywhere. One stale platform re-buys your own customers. +- Judge broad/algorithmic tiers on incremental performance, not platform-attributed CPA (see **attribution**). +- Check overlap when adding audiences on the same platform. Two campaigns bidding on the same users compete against themselves. +- Preflight every new audience campaign before enabling it (see **campaign-preflight**). diff --git a/packages/claude-directory/skills/audience/SKILL.md b/packages/claude-directory/skills/audience/SKILL.md new file mode 100644 index 0000000..ca6b263 --- /dev/null +++ b/packages/claude-directory/skills/audience/SKILL.md @@ -0,0 +1,16 @@ +--- +name: audience +description: Review existing audiences and plan account-specific targeting, exclusions, and targeting and list requirements. +--- + +# Audience planning + +Confirm the workspace and accounts with `list_connected_accounts`. Inspect existing audiences with `list_audiences`, preserving their returned IDs and platform scope. + +Clarify who should see the campaign, which geographies are in scope, and which groups should be excluded. For first-party segments, document source, consent, recency, match quality, and suppression requirements. For expansion, define a seed and a test hypothesis without claiming a list has been built or synchronized. + +Use `research_campaign_opportunity` to inform the campaign brief where the returned data supports it. Do not infer person-level records or audience membership from aggregate research. + +Audience creation, list upload, and synchronization require the platform’s own interface. Ask the user to complete those steps if needed, then re-read `list_audiences`. For supported campaign targeting fields, record existing audience IDs with `create_campaign_plan` and `upsert_plan_entity`; follow **launch** for preflight and approval. + +Report what exists, what is proposed, and what still needs manual setup. Never mix audiences from different workspaces. diff --git a/packages/claude-directory/skills/bid-optimization/SKILL.md b/packages/claude-directory/skills/bid-optimization/SKILL.md new file mode 100644 index 0000000..e552788 --- /dev/null +++ b/packages/claude-directory/skills/bid-optimization/SKILL.md @@ -0,0 +1,36 @@ +--- +name: bid-optimization +description: Recommend bid and bid-strategy changes from performance data — CPC targets, device and audience modifiers, and target CPA/ROAS moves. Use when a user wants to lower CPCs, hit a target CPA or ROAS, or adjust bids across campaigns. +--- + +# Bid Optimization + +Move bids toward the target CPA or ROAS using real performance, not guesses. Reads do not require approval; changing bids spends differently, so confirm before shipping. + +Confirm the account: `list_connected_accounts`. + +## 1. Read performance with enough data + +Pull recent campaign performance with `pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, or `pull_reddit_ads_performance`. Use segment detail only if returned; request platform exports for missing breakdowns. State the period and conversion volume before making a recommendation. + +## 2. Diagnose before you move + +- Segments beating the target CPA/ROAS with volume to spare want more bid or budget. +- Segments far above target with real spend want less, or a match-type or negative fix first (see **negative-keywords**). +- High CPCs with poor relevance are a Quality Score or targeting problem, not a bid problem. Fixing the ad or keyword often beats raising the bid. + +## 3. Choose the lever + +- **Bid strategy:** manual vs target CPA vs target ROAS vs maximize conversions. Match it to data volume and the goal. +- **Modifiers:** device, audience, location, and schedule (see **dayparting**) adjust bids where performance actually differs. +- Move in steps, not leaps. Large automated-bid target swings reset learning and destabilize delivery. + +## 4. Apply on approval + +Apply budget changes with `update_campaign_budget` on approval. Hand bid-strategy and modifier changes on existing campaigns to the platform UI unless the live plan schema supports them; confirm and re-measure. + +## Rules + +- Never chase a target off a few conversions. Minimum data first. +- Step changes, then wait. Do not thrash bids day to day. +- Report what moved, by how much, and the expected effect on CPA/ROAS. Scale winners with the **optimize** skill. diff --git a/packages/claude-directory/skills/campaign-preflight/SKILL.md b/packages/claude-directory/skills/campaign-preflight/SKILL.md new file mode 100644 index 0000000..ade139f --- /dev/null +++ b/packages/claude-directory/skills/campaign-preflight/SKILL.md @@ -0,0 +1,63 @@ +--- +name: campaign-preflight +description: Pre-launch checks for an ad campaign — structure, conversion tracking, UTMs, geo targeting, negatives, budget, and schedule. Use before enabling a paused campaign, after building a new one, or when a user asks to review or check campaign settings before spend. +--- + +# Campaign Pre-Flight + +Catch the mistakes that waste budget before a campaign goes live. Reads do not require approval. Run the full checklist, report pass/warn/fail per item, and fix issues before enabling. Nothing that spends money ships without explicit approval. + +Confirm the account first: `list_connected_accounts`. Use the real account and campaign IDs the tools return. + +## 1. Structure is complete (blocking) + +A campaign that looks Active does not serve if pieces are missing. Read the real structure and confirm every level exists and is enabled: + +- Use `audit_account_structure` and `get_campaign_plan` for available structure and asset details. Run `run_launch_preflight` on the plan. Confirm required ad groups, keywords, budgets, and assets only where the returned evidence supports them; obtain platform exports for missing detail. +- **Other platforms:** `list_campaigns(platform=...)` plus the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`) and the matching platform playbook skill to confirm ad sets/ad groups, ads, and budget. + +If a required level is missing, do not enable. Fix it first. + +## 2. Conversion tracking exists and is attached (blocking for conversion goals) + +Without tracking, the spend is blind and smart bidding cannot work. + +- Use `ga4_get_conversions`, `ga4_get_report`, and `get_pixel_health` for available conversion evidence. A configured action does not prove that a tag is firing; mark missing verification explicitly. +- Missing verified tracking on a conversion campaign is a stop. Once conversion goals are established, use MAXIMIZE_CONVERSIONS for Google Search conversion campaigns; do not substitute unconstrained Max Clicks. Consider Target CPA when conversion volume supports it. + +## 3. UTMs on every ad (blocking) + +Untagged traffic cannot be attributed in GA4 or a CRM. Confirm each ad's final URL carries UTMs. Standard shape: + +- `utm_source` = platform (`google`, `microsoft`, `meta`, `linkedin`, `reddit`, `tiktok`, `x`) +- `utm_medium` = `cpc` (search), `paid-social` (social), `display`, or `video` +- `utm_campaign` = campaign name, slugified +- `utm_content` = ad group or ad set, `utm_term` = keyword or variant (optional) + +See the **utm-builder** skill for consistent tags. + +## 4. Geo targeting is set and correct (blocking) + +A campaign with no location criteria serves everywhere, including low-quality and high-fraud geographies. This is the classic silent budget leak. + +- Confirm location criteria exist and match the intended market. A campaign named for one country with no location criteria is the red flag. +- Prefer presence-based matching (people in the location) over interest-based unless the goal truly needs it. +- Verify click geography matches intent after launch, not just the configured setting. + +## 5. Creative assets meet platform requirements + +Read available asset details from `get_campaign_plan`, `list_creative_assets`, or a platform export. Verify the platform’s required asset counts, character limits, approved claims, and destination. Report missing evidence rather than assuming compliance. + +- Prefer fewer concise, relevant RSA headlines that express search intent, the offer, and intentional audience qualification. Do not impose an 11-headline floor or pad to 15. +- Ad Strength is not a launch gate. Do not claim that a lower rating proves throttling or add filler to raise the rating. +- For each format, include its required assets and the placements in the approved plan. Optional slots are useful only when the content adds a distinct, relevant message. + +## 6. Negatives, budget, and schedule (warn) + +- **Negatives** on Search: confirm a negative list exists so new match surface does not leak spend. See the **negative-keywords** skill. +- **Budget:** sane daily amount, guarded against a fat-finger 10 to 100x. Set a max CPC on manual bidding. +- **Schedule:** if the offer is business-hours only, confirm an ad schedule rather than 24/7. + +## Report + +Output a short table: each check as PASS, WARN, or FAIL with a one-line note, then a clear recommendation (enable, or fix these first). Blocking fails (structure, tracking, UTMs, geo, missing required creative assets) must clear before you enable. Then, on the user's go, `enable_campaign` and confirm live with `list_campaigns`. diff --git a/packages/claude-directory/skills/campaign-structure-auditor/SKILL.md b/packages/claude-directory/skills/campaign-structure-auditor/SKILL.md new file mode 100644 index 0000000..6f99231 --- /dev/null +++ b/packages/claude-directory/skills/campaign-structure-auditor/SKILL.md @@ -0,0 +1,29 @@ +--- +name: campaign-structure-auditor +description: Audit a Google Ads account's structure — ad group focus, keyword duplication, match-type distribution, cross-campaign conflicts, and naming consistency. Use when a user wants an account audit, asks why an account is hard to manage, or suspects duplicate keywords or messy structure. +--- + +# Campaign Structure Auditor + +Good structure is why an account is cheap to run and easy to read. This audit finds the structural drag before it costs money. Reads only; fixes are separate, approved changes. + +Confirm the account with `list_connected_accounts`, inspect structure with `audit_account_structure`, and use `pull_google_ads_performance` to prioritize findings by spend. Request a platform export for any keyword, negative, or match-type details not returned. + +## What to audit + +- **Ad group focus:** each ad group should own a tight theme so its RSAs stay relevant. Sprawling ad groups with unrelated keywords drag Quality Score and CTR. +- **Keyword duplication:** the same keyword in multiple ad groups or campaigns makes them bid against each other and inflate CPC. Flag duplicates and where they overlap. +- **Match-type distribution:** a healthy mix with control. Broad-heavy accounts without strong negatives leak spend (see **negative-keywords**); all-exact accounts miss reachable demand. +- **Cross-campaign conflict:** two campaigns competing for the same query. Resolve with campaign-level negatives so each owns its intent. +- **Negatives coverage:** shared and campaign-level lists in place, or new match surface is unguarded. +- **Naming consistency:** a readable, consistent naming grammar. Inconsistent names make the account unmanageable and break UTM reconciliation (see **utm-builder**). + +## Report + +Rank findings by cost and effort: what wastes the most spend, what is the quickest structural fix. For each, name the specific campaigns or ad groups and the concrete change. Then, on approval, apply fixes through the plan tools and re-audit. + +## Rules + +- Point to real campaigns, ad groups, and keywords with the IDs the tools return; never generalize without evidence. +- Weight findings by spend impact, not tidiness for its own sake. +- Structural fixes are approved changes, applied one at a time and re-checked. diff --git a/packages/claude-directory/skills/competitor-analysis/SKILL.md b/packages/claude-directory/skills/competitor-analysis/SKILL.md new file mode 100644 index 0000000..88161b3 --- /dev/null +++ b/packages/claude-directory/skills/competitor-analysis/SKILL.md @@ -0,0 +1,31 @@ +--- +name: competitor-analysis +description: Research competitors' advertising — the creative they run, their angles and offers, and where they compete for the same auctions. Use when a user wants competitive research, creative inspiration, to see what rivals are running, or market positioning for ads. +--- + +# Competitor Analysis + +See what competitors run so you can position against it, not copy it. Reads and research only; this does not spend money. + +## 1. Find what they run + +Use `research_campaign_opportunity` for available research. Ask for public ad-library links or examples when the tool does not return competitor ads. Separate observed messaging from hypotheses about performance; longevity alone does not establish profitability. + +## 2. Read the pattern, not the pixels + +- **Angles:** what problem they lead with, what proof they show, what objection they answer. +- **Offers:** price framing, trial, guarantee, urgency. +- **Formats and platforms:** where they concentrate and where they are absent (absence is opportunity). +- **Longevity:** creative running for months is a signal; a one-week test is not. + +## 3. Turn it into your move + +- Where they are strong, differentiate on a real advantage, do not match message for message. +- Where they are absent (a platform, an audience, an angle), that is open ground. +- On search, competitor brand terms and the same auctions may be worth contesting; weigh cost against intent. + +## Rules + +- Inform strategy; never copy creative or claims. Copying inherits their weaknesses and risks trademark issues. +- Separate what is proven (long-running, repeated) from what is a test. +- Report the angles and gaps you found and the specific move you would make, then take it to the **ad-copy-generation** or **launch** skill. diff --git a/packages/claude-directory/skills/connect/SKILL.md b/packages/claude-directory/skills/connect/SKILL.md new file mode 100644 index 0000000..ad3a015 --- /dev/null +++ b/packages/claude-directory/skills/connect/SKILL.md @@ -0,0 +1,14 @@ +--- +name: connect +description: Connect ad accounts and GA4 through the Synter browser flow and verify the selected workspace. +--- + +# Connect accounts + +Use `get_connection_status` and `list_connected_accounts` to identify the workspace and existing accounts. Report the platform, account name, and account ID. + +Direct the user to https://synterai.com/settings/credentials to connect the required platform or GA4 account in the browser. Afterward, re-run `list_connected_accounts` and `verify_platform_accounts`. For GA4, confirm access with `ga4_get_properties`. Use `get_pixel_health` for the available tracking diagnostics; do not treat account access as proof of a working conversion event. + +If access fails, confirm the selected workspace and the connected user's platform permissions. Reconnect through browser OAuth when needed. Never collect keys, tokens, or authorization codes in chat. For accounts that need provisioning help, link https://synterai.com/contact. + +Confirm the workspace again before any write. Keep account IDs, audiences, tracking, destinations, and credentials scoped to that workspace. diff --git a/packages/claude-directory/skills/conversion-tracking/SKILL.md b/packages/claude-directory/skills/conversion-tracking/SKILL.md new file mode 100644 index 0000000..7c13e57 --- /dev/null +++ b/packages/claude-directory/skills/conversion-tracking/SKILL.md @@ -0,0 +1,14 @@ +--- +name: conversion-tracking +description: Verify available conversion and pixel diagnostics before launch; hand off tag changes to the account owner. +--- + +# Conversion tracking + +Confirm the account with `list_connected_accounts` and `verify_platform_accounts`. Use `ga4_get_properties` to select the right property, `ga4_get_conversions` to inspect configured conversions, and `ga4_get_report` to check recorded events over a stated period. Read `get_pixel_health` for available pixel diagnostics. + +A configured conversion is not proof that the right event fires or that the campaign optimizes toward it. Compare the intended business action with the evidence returned. Use `run_launch_preflight` for a campaign plan. Mark fields and checks unavailable from the tools as unverified. + +For missing tags, event routing, or consent settings, describe the exact gap and have the account owner make the change in the platform or tag manager. This plugin does not publish tags or configure pixel destinations. + +After changes, repeat the reads and confirm a test conversion in the platform's own diagnostics. Do not launch or scale until required tracking checks pass. Report the workspace, account, property, conversion action, observed evidence, and remaining gaps. diff --git a/packages/claude-directory/skills/creative-fatigue-detector/SKILL.md b/packages/claude-directory/skills/creative-fatigue-detector/SKILL.md new file mode 100644 index 0000000..2a70f8d --- /dev/null +++ b/packages/claude-directory/skills/creative-fatigue-detector/SKILL.md @@ -0,0 +1,46 @@ +--- +name: creative-fatigue-detector +description: "Diagnose creative fatigue; refresh cadence; when to swap/rotate ads; frequency caps; CTR drop / CPM rise signals; per-platform creative lifespans." +--- + +# Creative Fatigue Detection & Refresh Cadence + +Use these typical starting thresholds; prefer the account's own history to decide when to refresh creatives BEFORE performance collapses. Most teams refresh too late — the goal is to act on early warnings, not wait for CPA to blow up. + +## Early Warning Signals (act on these BEFORE CPA spikes) + +| Signal | Threshold | Action | +|--------|-----------|--------| +| CTR declining | 20%+ drop from peak over 3 days | Queue replacement creative | +| CPM rising | 15%+ increase with same targeting | Expand audience or refresh creative | +| Frequency climbing | Above 2.5 for prospecting | Introduce new creative variations | +| Frequency critical | Above 3.0 for retargeting | Immediate creative swap required | +| Hook rate dropping | Below 20% after previously >25% | New hooks needed | +| CPA creep | 15%+ above target for 3+ days | Test new angles, not just variations | + +## Creative Lifespan by Platform (plan refreshes proactively) + +| Platform | Format | Typical Lifespan | Refresh Cadence | +|----------|--------|------------------|-----------------| +| TikTok | In-feed video | 1-3 weeks | Weekly new creative | +| TikTok | Spark Ads | 2-4 weeks | Bi-weekly refresh | +| Meta | Reels | 2-3 weeks | Weekly new creative | +| Meta | Feed static | 1-2 weeks | 2x weekly refresh | +| Meta | Carousel | 3-4 weeks | Bi-weekly refresh | +| Google PMax | Mixed assets | 3-6 weeks | Monthly asset refresh | +| YouTube | In-stream | 4-8 weeks | Monthly refresh | +| LinkedIn | Sponsored content | 3-5 weeks | Bi-weekly refresh | + +## Anti-Fatigue Tactics + +- **Format rotation:** Cycle between video, static, carousel. When one format fatigues, another stays fresh. +- **Audience rotation:** Move fatigued creative to new audiences before retiring it entirely. +- **Iteration first:** Test new hooks on a winning body before reinventing the full concept. +- **Pipeline health:** Always maintain 3-5 concepts in dev, 3-5 in production, 3-5 in test queue, 5-10 running live. + +## Reading Signals Together + +A single signal can be noise; **two or more in the same week is a real fatigue event**: +- CTR dropping AND frequency climbing → fatigue confirmed, swap creative +- CPM rising alone with stable CTR → likely auction pressure, not fatigue +- Hook rate falling AND CPA creeping → reinvent hooks, don't just iterate variations diff --git a/packages/claude-directory/skills/creative-testing/SKILL.md b/packages/claude-directory/skills/creative-testing/SKILL.md new file mode 100644 index 0000000..b51b5a8 --- /dev/null +++ b/packages/claude-directory/skills/creative-testing/SKILL.md @@ -0,0 +1,34 @@ +--- +name: creative-testing +description: Structure and read creative A/B tests across platforms — set up variants, decide when a result is real, and pick winners without fooling yourself on noise. Use when a user wants to test ads, compare creative variants, run an A/B test, or choose a winning ad. +--- + +# Creative Testing + +Test to learn, then scale the winner. Most "winners" called early are noise. Reads do not require approval; launching variants spends money, so confirm before shipping. + +Confirm the account: `list_connected_accounts`. + +## 1. Test one thing at a time + +Change one variable per test (hook, image, headline, offer, format) so the result is attributable. Testing five things at once tells you nothing about which one moved the number. + +## 2. Set it up cleanly + +Write text variants with `create_ad_copy` and select existing approved visual assets with `list_creative_assets`. Keep the comparison controlled and give each variant enough data. Follow **launch** and **campaign-preflight** before deploying a test. + +## 3. Wait for enough data + +- Judge on conversions and CPA where volume allows, on CTR only as an early proxy. +- Do not call a winner off a few conversions or one day; small samples swing wildly. State the volume behind the call. +- On Meta, respect the Learning Phase before comparing (see **platform-meta**). + +## 4. Read and act + +Pull results per variant: the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`). Pick the winner on the metric that matters (usually CPA or ROAS, not CTR). Scale it with the **optimize** skill, retire the losers, and feed what you learned into the next test. + +## Rules + +- One variable per test; enough data before a verdict. +- Winner is decided on the conversion metric, not clicks, unless volume forces a proxy. +- Report the variants, the volume, the winner, and the next test to run. Watch winners for fatigue with the **creative-fatigue-detector** skill. diff --git a/packages/claude-directory/skills/creative/SKILL.md b/packages/claude-directory/skills/creative/SKILL.md new file mode 100644 index 0000000..c9f0f83 --- /dev/null +++ b/packages/claude-directory/skills/creative/SKILL.md @@ -0,0 +1,22 @@ +--- +name: creative +description: Prepare ad copy and creative briefs from the verified advertiser's brand and existing assets. Use for new ad concepts, variations, or creative refreshes. +--- + +# Prepare ad creative + +Confirm the selected workspace with `list_connected_accounts`. Resolve the advertiser's name, domain, offer, audience, and approved claims. Read [brand and safety guidance](../../context/brand-and-safety.md), then use `list_creative_assets` for available original assets. Ask for any missing logo, voice, or destination. + +For text ads, use `create_ad_copy` with the verified platform, audience, offer, and brand context. Give each variant a distinct testable idea, and preserve platform limits. For images, video, or audio, prepare a brief with the asset references, composition or shot list, script, format, and exact CTA. The directory connection does not expose image, video, audio generation, or asset upload; identify the production step still needed rather than claiming a generated file exists. + +Review the exact copy and available assets for brand match, substantiated claims, and placement requirements. Drafting does not authorize publication. Use the **launch** workflow only within an explicitly approved campaign scope and verify the effective campaign state after execution. + +```mermaid +flowchart LR + A["Verify advertiser"] --> B["Read assets and brief"] + B --> C["Draft copy or production brief"] + C --> D["Review exact output"] + D --> E["Approved campaign workflow"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E step; +``` diff --git a/packages/claude-directory/skills/dayparting/SKILL.md b/packages/claude-directory/skills/dayparting/SKILL.md new file mode 100644 index 0000000..ab4381f --- /dev/null +++ b/packages/claude-directory/skills/dayparting/SKILL.md @@ -0,0 +1,34 @@ +--- +name: dayparting +description: Analyze hour-of-day and day-of-week performance and set ad schedules and bid modifiers to spend when conversions happen. Use when a user wants to optimize ad scheduling, set dayparting, find the best times to run ads, or cut spend during dead hours. +--- + +# Dayparting and Ad Scheduling + +Spend when the account converts, not evenly around the clock. Reads do not require approval; schedule and modifier changes affect delivery, so confirm before shipping. + +Confirm the account: `list_connected_accounts`. + +## 1. Read time-segmented performance + +Review time-of-day and weekday performance only when the platform performance tool returns those breakdowns. Otherwise ask for a platform export. Compare conversions and CPA by time slot over a stable period, not just clicks. Schedule edits require the platform UI when no supported plan field exists. + +## 2. Find the real pattern + +- Slots with strong conversion volume and CPA at or below target want to keep or gain bid. +- Slots with spend and clicks but no conversions are candidates to reduce or exclude. +- Watch for thin cells; a single quiet hour is not a trend. Judge on volume, not one day. + +## 3. Set the schedule + +Build an ad schedule with bid modifiers per slot: raise where it converts, lower or pause where it does not. Keep changes proportionate; do not zero out a slot on weak data. + +## 4. Apply on approval + +Hand the schedule and modifiers to the account owner for the platform UI unless the live plan schema supports them; confirm after it is applied. Re-pull after a couple of weeks to check the pattern held; buyer behavior shifts seasonally (see **kill-scale-rules** for scaling discipline). + +## Rules + +- Base every slot decision on conversions with volume, not clicks or one-off days. +- Account for the account's real time zone, not yours. +- Report the schedule set and the expected shift in blended CPA. diff --git a/packages/claude-directory/skills/executive-reporting/SKILL.md b/packages/claude-directory/skills/executive-reporting/SKILL.md new file mode 100644 index 0000000..6a96c34 --- /dev/null +++ b/packages/claude-directory/skills/executive-reporting/SKILL.md @@ -0,0 +1,30 @@ +--- +name: executive-reporting +description: Build C-suite and board ad reports — blended ROAS, month-over-month and year-over-year, pacing, and a plain-English narrative of what happened and what to do. Use when a user wants an executive summary, a board report, or a monthly performance review across platforms. +--- + +# Executive Reporting + +Write for a reader who does not live in the ad accounts. Lead with the result and the decision, not the dashboard. Reads only; this produces a report, it does not change spend. + +Confirm the account: `list_connected_accounts`, then pull real numbers across platforms: the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`), `get_attribution` for the cross-platform view, `ga4_get_report` for outcomes. Reconcile platform self-reports before quoting a total (see **attribution**). + +## What an exec report says + +- **Headline:** blended spend, conversions or revenue, blended ROAS or CPA, and the one sentence that matters. ("Spend held flat; leads up 22% as we shifted budget to search.") +- **Trend:** month-over-month and, where data exists, year-over-year. Show direction and magnitude, not a wall of rows. +- **By channel:** a short table of the platforms that matter, with spend, result, and efficiency. Group the long tail. +- **Pacing:** on track, over, or under budget for the period, and the projected finish. +- **Decisions:** what you changed, what you recommend next, and what needs their call. This is the part they read. + +## How to present it + +- Narrative first, tables second. Every number earns its place; cut vanity metrics. +- Round to what matters at their altitude; no false precision. +- Deliver a copyable summary and table in the conversation. The plugin does not create external documents or send reports. Use **report** for operator-level per-channel detail. + +## Rules + +- Never fabricate a number or a trend. If data is missing or a platform is unreconciled, say so plainly. +- State the attribution basis and the period behind blended figures. +- Lead with the result and the recommendation; the executive should get the point in the first two lines. diff --git a/packages/claude-directory/skills/first-party-data-strategy/SKILL.md b/packages/claude-directory/skills/first-party-data-strategy/SKILL.md new file mode 100644 index 0000000..1aeb40d --- /dev/null +++ b/packages/claude-directory/skills/first-party-data-strategy/SKILL.md @@ -0,0 +1,61 @@ +--- +name: first-party-data-strategy +description: Build a post-cookie first-party data strategy — Enhanced Conversions, server-side tracking, Consent Mode v2, and planning how to use owned data for targeting. Use when a user wants to recover lost conversion data, set up Enhanced Conversions or CAPI, implement consent mode, or plan for cookie deprecation. +--- + +# First-Party Data Strategy + +Third-party cookies are gone; the accounts that keep measuring are the ones that collect, consent, and send their own data. Build that pipeline in order: audit, capture, consent, then plan how to use it. + +Confirm the workspace with `list_connected_accounts` and `get_connection_status`; read `get_pixel_health` for the available tracking diagnostics. + +## 1. Audit what you collect today + +Inventory every point where you capture an identifier: forms, signups, purchases, email lists, CRM records, in-app events. For each, note the identifier (email, phone, address), consent status, and freshness. Check what conversion data actually reaches the platforms: `ga4_get_conversions` and `ga4_get_report` for the site side, the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`) for what each platform claims. Big gaps between GA4 and platform numbers usually mean tracking loss, not attribution magic — see **conversion-tracking**. + +## 2. Set up Enhanced Conversions and CAPI + +Hashed first-party identifiers recover conversions that cookies alone miss. + +- **Google Enhanced Conversions:** send SHA-256 hashed email/phone/address with the conversion tag. Normalize before hashing: lowercase, trim, E.164 phone format. Typical recovery is 5-15% more attributed conversions. +- **Meta Conversions API:** send server-side events with hashed identifiers plus `fbc`/`fbp` cookie values, and deduplicate against the browser pixel with a shared `event_id`. Typical recovery is 15-30%. +- LinkedIn, TikTok, and Reddit have equivalent server-side conversion APIs; the same hashing and dedup rules apply. + +Inspect the available diagnostics with `get_pixel_health`. Have the account owner make tag, event-routing, and consent changes in the platform UI or tag manager, then verify with a test event. The plugin cannot publish tags. + +## 3. Move tracking server-side + +Client-only tags lose data to ad blockers and 7-day browser cookie caps. A server-side GTM container on a first-party subdomain (track.yourdomain.com) extends cookie lifespan to 1-2 years, enriches events before forwarding (LTV, customer segment), and sends once to the platforms Synter connects to. This is the single highest-leverage tracking improvement for a returning-visitor business. + +## 4. Implement Consent Mode v2 + +Required for EEA traffic since March 2024. Two choices: + +- **Basic:** tags do not fire without consent. Simple, but every unconsented user is invisible. +- **Advanced (recommended):** cookieless pings fire even when consent is denied and Google models the missing conversions, recovering roughly half to two-thirds of them. + +Set default consent state to denied before the CMP loads, update on user choice, and gate the four ad parameters (`ad_storage`, `ad_user_data`, `ad_personalization`, `analytics_storage`). Verify consent signals in Google Ads diagnostics and GA4 admin after deploying. + +## 5. Know your match rates + +Uploaded lists only work as well as they match. Rough expectations: hashed email alone matches 40-60% on Google and 50-70% on Meta; adding phone pushes combined match to 55-80%. LinkedIn matches better on work email than personal. Improve matches by sending multiple identifiers per record and normalizing before hashing. If a synced list delivers to far fewer users than you uploaded, fix the data before blaming the platform. + +## 6. Prepare the data for upload in each platform's own tools + +Collected data earns its keep in audiences: + +- Review available lists with `list_audiences`; the account owner handles synchronization and suppression updates in the platform UI. +- Define a seed from the best customers and hand off its construction to the platform UI; see **audience**. +- Always maintain suppression lists — existing customers and recent converters excluded from acquisition. Refresh at least weekly. +- Pass conversion value where you have it so value-based bidding has something to optimize toward (see **bid-optimization**). + +## 7. Verify the pipeline end to end + +After changes, compare site events with `ga4_get_report` and available platform performance results, then inspect attribution differences with `get_attribution`. Use `get_spend_reconciliation` for spend records separately. Mark unsupported event-level checks as unverified. + +## Rules + +- Never send unhashed PII to an ad platform. Hash client-side or server-side, always. +- Consent is not optional in regulated geographies; denied-by-default is the safe default state. +- Measure recovery: compare attributed conversions for 2-4 weeks before and after each change so you know what each fix earned. +- Suppression lists are part of the strategy, not an afterthought. Stale lists burn budget on people who already bought. diff --git a/packages/claude-directory/skills/geo-integrity-audit/SKILL.md b/packages/claude-directory/skills/geo-integrity-audit/SKILL.md new file mode 100644 index 0000000..abdecd6 --- /dev/null +++ b/packages/claude-directory/skills/geo-integrity-audit/SKILL.md @@ -0,0 +1,16 @@ +--- +name: geo-integrity-audit +description: Check intended geography against available campaign settings and delivery evidence; identify unverified targeting before launch. +--- + +# Geo integrity audit + +Confirm the workspace with `list_connected_accounts`, then read `list_campaigns` and `audit_account_structure`. For a planned campaign, use `get_campaign_plan` and `run_launch_preflight`. + +Compare the intended geography with the location criteria, exclusions, and presence settings actually returned. A campaign name is not targeting evidence. Check every ad set or ad group when the data is available. Missing fields mean UNVERIFIED, not worldwide delivery or a passed check. + +Use `pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, or `pull_reddit_ads_performance` for delivery evidence. Request geography only when the tool schema supports that breakdown. Cross-check site engagement by country with `ga4_get_report`. If targeting or delivery detail is unavailable, request an export from the platform UI. + +Investigate spend outside the intended market, unusually cheap clicks with weak engagement, and traffic spikes. These are signals to investigate, not proof of fraud. + +Report per campaign: intended geography, verified settings, evidence period, out-of-market delivery where measurable, and missing evidence. Propose corrections through supported campaign-plan fields and the **launch** approval flow, or hand off unsupported settings to the platform UI. Re-read settings and delivery after the change. diff --git a/packages/claude-directory/skills/google-ads-quality-score/SKILL.md b/packages/claude-directory/skills/google-ads-quality-score/SKILL.md new file mode 100644 index 0000000..09580dc --- /dev/null +++ b/packages/claude-directory/skills/google-ads-quality-score/SKILL.md @@ -0,0 +1,51 @@ +--- +name: google-ads-quality-score +description: Diagnose and improve Google Ads Quality Score — Expected CTR, Ad Relevance, and Landing Page Experience — to cut CPCs and lift Ad Rank. Use when CPCs look high for the vertical, impression share is capped by rank, or keywords sit at QS 5 and below. +--- + +# Google Ads Quality Score + +Quality Score is a keyword-level 1-10 diagnostic built from three rated components: Expected CTR, Ad Relevance, and Landing Page Experience. Low QS means you pay more per click for the same position — fixing the weakest component is the cheapest CPC reduction available. + +Confirm the account: `list_connected_accounts`. + +## 1. Pull QS with spend context + +Use `pull_google_ads_performance` and `audit_account_structure` to prioritize campaign issues. Request a Google Ads export for keyword-level Quality Score and its components if the returned data lacks them. Never infer a component score from campaign averages. + +Rank the fix list by cost, not by QS: a QS 3 keyword spending $4K/month matters more than twenty QS 4 keywords spending nothing. Pull the below-average components on the highest-spend keywords first. + +## 2. Read the components + +- **Expected CTR below average**: your ad gets clicked less than competitors' for the same query. Ad copy problem. +- **Ad Relevance below average**: the ad text does not match the keyword's intent. Structure problem. +- **Landing Page below average**: slow, thin, or mismatched page. Post-click problem. + +Rough mechanics worth knowing: Ad Rank scales with bid times quality, and actual CPC is the ad rank of the advertiser below you divided by your quality. Moving a keyword from QS 4-5 to QS 7 typically cuts its CPC on the order of 25-30% — quantify the monthly savings before recommending work. + +## 3. Fix Expected CTR + +Write RSA variants with `create_ad_copy`: use relevant keywords, concrete differentiators, and distinct headlines. Compare current text and asset counts using `list_creative_assets` where available, or request a platform export. Follow **launch** before any deployment. + +## 4. Fix Ad Relevance + +Restructure to tightly themed ad groups: if no headline in the ad group contains the keyword or a close synonym, the keyword is in the wrong ad group. Split mixed-intent groups (one theme per group), then write ads that name the theme. Keyword insertion helps only when the group is already tight. Expect 1-2 weeks after restructuring. Query bleed between groups is a negatives problem — see **negative-keywords**. + +## 5. Fix Landing Page Experience + +- Speed: LCP under 2.5s, mobile-usable, no intrusive interstitials. +- Message match: page headline mirrors the ad promise; ad-group keywords appear in the page copy above the fold. +- Trust: HTTPS, visible contact info, clear offer details and policies. + +If the page needs replacing, provide a copy and usability brief for the site owner. The plugin does not publish landing pages. Recheck the destination before launch. + +## 6. Track it over time + +Google keeps no QS history, so snapshot weekly with the same GAQL query and track impression-weighted average QS, percent of spend on QS 7+ keywords, and percent of keywords at QS 3 and below. Report movement against those, not single-keyword anecdotes. + +## Rules + +- Prioritize by wasted spend, not by score. Fix the expensive keywords first. +- Competitor-brand keywords naturally sit at QS 3-5; judge them on CPA, not QS. +- One component per ad group per cycle, then wait for the rating to update before layering more changes. +- QS is a diagnostic, not an auction input — never chase 10/10 on keywords that already hit CPA targets. Bid moves belong to **bid-optimization**. diff --git a/packages/claude-directory/skills/google-shopping-optimizer/SKILL.md b/packages/claude-directory/skills/google-shopping-optimizer/SKILL.md new file mode 100644 index 0000000..6f80228 --- /dev/null +++ b/packages/claude-directory/skills/google-shopping-optimizer/SKILL.md @@ -0,0 +1,58 @@ +--- +name: google-shopping-optimizer +description: Optimize Google Shopping — product feed quality, titles, Merchant Center disapprovals, supplemental feeds, custom-label segmentation, and Shopping vs Performance Max structure. Use when Shopping impression share is low, products are disapproved, or feed titles and labels need work. +--- + +# Google Shopping Optimizer + +Shopping performance is mostly feed quality. Fix titles, identifiers, images, and price accuracy before touching bids or structure. + +Confirm the account with `list_connected_accounts`. Pull campaign performance with `pull_google_ads_performance`; request Merchant Center or Google Ads exports for product-level data not returned. + +## 1. Audit the feed + +Export the feed and check, in order of revenue contribution: + +- Missing GTINs on branded products (required; use `identifier_exists = false` only for genuinely custom or handmade items). +- Short titles (under ~70 characters) missing brand, product type, or key attributes. +- Thin descriptions, missing `google_product_category` or `product_type`. +- Main images with watermarks, text overlays, or lifestyle shots where a white-background product shot belongs (800x800+ recommended). +- Feed price or availability that does not match the landing page — the top disapproval cause. + +Grade each top product: optimized, needs work, or at risk of disapproval. Fix by revenue, not alphabetically. + +## 2. Rewrite titles + +Structure: Brand + Product Type + Key Attributes (color, size, material, gender) + Model/Variant. Front-load the important terms — Shopping truncates around 70 characters. No ALL CAPS, no promo text ("SALE", "SPECIAL OFFER"), no symbols. Aim for 70-100 characters, max 150. + +## 3. Clear disapprovals + +Triage by reason, fastest first: + +- **Price/availability mismatch**: sync feed to page, enable automated item updates. Instant class of fix. +- **Missing GTIN/MPN**: bulk-add via supplemental feed; source GTINs from the manufacturer or GS1. +- **Image issues**: replace with clean white-background images; product should fill most of the frame. +- **Policy violations**: check the landing page and image against the restricted-content policies (see **campaign-preflight** before relaunch). + +Request re-review; most complete within 24-72 hours. Track reinstatement rate weekly. + +## 4. Use supplemental feeds for speed + +A supplemental feed (a connected sheet keyed on product `id`) overrides primary-feed values without touching the source system. Use it to fix titles, add GTINs, set `sale_price`, and stamp custom labels — same-day fixes instead of waiting on a feed pipeline. + +## 5. Segment with custom labels + +The five `custom_label` slots are your only campaign-structure handles. A workable scheme: label 0 = performance tier (hero / mid / long tail by trailing revenue), label 1 = margin tier, label 2 = seasonality, label 3 = price band, label 4 = competitive price position. Then structure campaigns by label — hero products get the budget and the aggressive ROAS target, long tail runs cheap. Apply budget moves with `update_campaign_budget` and cross-product allocation with `optimize_budget`. + +## 6. Shopping vs Performance Max + +- **Standard Shopping**: full search-term visibility, campaign-level negatives, product-level bid control. Best for mature accounts that need control. +- **PMax**: all Google channels, better new-product discovery, but limited query visibility and account-level negatives only. Best for growth and catalog breadth — see **performance-max-optimizer**. +- The usual right answer is hybrid: Standard Shopping on hero SKUs, PMax on the rest of the catalog, compare ROAS by product group after 30 days. Watch for PMax eating brand queries when comparing. + +## Rules + +- Feed fixes before bid fixes. A bad title wastes every dollar bid behind it. +- Price mismatch is the one issue that gets accounts suspended — verify it first and keep automated item updates on. +- Never put promotional text or watermarks on main images. +- State expected impact and timeline for each fix batch, then re-check impression share after two weeks. diff --git a/packages/claude-directory/skills/help/SKILL.md b/packages/claude-directory/skills/help/SKILL.md new file mode 100644 index 0000000..f4f76c6 --- /dev/null +++ b/packages/claude-directory/skills/help/SKILL.md @@ -0,0 +1,14 @@ +--- +name: help +description: Explain the bundled skills, supported MCP tools, OAuth setup, and support options. +--- + +# Synter help + +This plugin connects to Synter's hosted advertising service over OAuth. Use **quickstart** to sign in and **connect** to verify accounts. + +Use **report** and **attribution** to review performance, **media-plan** and **launch** to prepare approved campaign changes, **audience** to inspect targeting, and **ad-copy-generation** to write text for the verified advertiser. Read [brand and safety guidance](../../context/brand-and-safety.md) before writing copy or proposing spend. + +This directory package uses static instructions and the declared remote MCP connection. Account operations run through Synter MCP tools. Use only tools in the bundled [tool directory](../../tools/directory-tools.json) and the live server schema; report unavailable capabilities plainly. Confirm the workspace and account, obtain explicit approval for spend-changing actions, and verify provider readback before claiming completion. + +[Privacy](https://synterai.com/privacy) · [Support](https://synterai.com/contact) diff --git a/packages/claude-directory/skills/incrementality-testing/SKILL.md b/packages/claude-directory/skills/incrementality-testing/SKILL.md new file mode 100644 index 0000000..f0ed5f5 --- /dev/null +++ b/packages/claude-directory/skills/incrementality-testing/SKILL.md @@ -0,0 +1,60 @@ +--- +name: incrementality-testing +description: Design and run incrementality tests that measure true lift from ads — geo holdouts, platform lift studies, significance math, and iCPA decisions. Use when a user wants to know whether ads actually cause conversions, design a holdout test, or decide budget based on real lift. +--- + +# Incrementality Testing + +Attribution measures correlation; incrementality measures causation. The question is always the same: would this conversion have happened without the ad? + +Start with `measure_incrementality` where the platform and data support it; design a manual test when it does not. + +## 1. Define the question + +Pick one testable claim: channel incrementality ("does Display drive anything?"), budget incrementality ("does doubling spend double conversions?"), or tactic incrementality ("is retargeting real?"). One variable per test. Pull the baseline first: the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`) for spend and attributed conversions, `ga4_get_report` for a platform-neutral conversion count. + +## 2. Choose the method + +- **Geo holdout:** run ads in test regions, go dark in matched control regions. The gold standard, but needs regional volume. Pause delivery in control geos via `pause_campaign` or geo exclusions. +- **Platform lift study:** Meta and Google both offer native conversion-lift experiments with randomized holdout groups. Least effort, but you trust the platform to grade its own homework. +- **Ghost ads / PSA:** serve an unrelated ad to the control group so both groups pass through the same auction. Works on any platform, costs the PSA impressions. + +## 3. Design it so the answer is trustworthy + +- **Match regions** on population, historical CVR, revenue per conversion, and seasonality. Validate with 8-12 weeks of history: if historical CVRs differ by more than ~5%, pick different regions. +- **Size the control** at 20-30% of the addressable market. +- **Duration:** minimum 4 weeks for most channels; 2 weeks only for high-volume search or Meta; add 50-100% for long sales cycles. Avoid holidays or extend through them. +- **Sample size:** small lifts need big audiences. Detecting a 20% lift on a 1% baseline CVR takes roughly 400K users; a 10% lift takes 1.5M. If you cannot reach the numbers, test a bigger intervention instead of a subtle one. + +## 4. Run it clean + +Keep other inputs stable during the test: avoid creative swaps, unrelated budget changes, or new campaigns in test/control markets. Read `list_campaigns` regularly to catch drift and measure outcomes with GA4. Configure any automated alerts in the platform UI. + +## 5. Analyze + +``` +Lift = (CVR_test - CVR_control) / CVR_control +Incremental conversions = Conversions_test - (Exposed_test x CVR_control) +iCPA = Test spend / Incremental conversions +iROAS = Incremental revenue / Test spend +``` + +Run a two-proportion z-test on test vs control CVR and report the lift with its 95% confidence interval, not just the point estimate. A lift whose interval crosses zero is not a result. + +## 6. Decide + +Compare iCPA to attributed CPA: + +- **iCPA within ~1.5x of CPA:** highly incremental. Scale it (see **kill-scale-rules**). +- **1.5-3x:** moderately incremental. Hold and optimize. +- **3-5x:** weak. Shift budget with `optimize_budget` and re-test the reallocation. +- **5x+:** the channel is mostly claiming credit for organic conversions. Cut it or keep it only for awareness. + +Brand search typically tests at 15-35% incrementality, non-brand search 40-65%, display prospecting 5-20%, retargeting 20-45%. If your result lands far outside these ranges, re-check the test before celebrating or panicking. + +## Rules + +- One variable per test. A test that changes budget, creative, and audience proves nothing. +- Neutral measurement only. The platform being tested does not get to score the test. +- Pre-register the success threshold before launch; do not move the goalposts after seeing data. +- Feed results back into planning: incrementality-adjusted returns belong in **mmm-budget-planner** and **attribution** conversations, not in a slide graveyard. diff --git a/packages/claude-directory/skills/keyword-research/SKILL.md b/packages/claude-directory/skills/keyword-research/SKILL.md new file mode 100644 index 0000000..d90e434 --- /dev/null +++ b/packages/claude-directory/skills/keyword-research/SKILL.md @@ -0,0 +1,37 @@ +--- +name: keyword-research +description: Find high-intent, low-waste keywords for Search campaigns and add them to the right ad groups. Use when a user wants keyword ideas, to expand a Search campaign, improve targeting, or find terms to bid on for Google or Microsoft Ads. +--- + +# Keyword Research + +Find terms real buyers search, group them by intent, and add them where they convert. Reads do not require approval; adding keywords to a live campaign is a change, so confirm before shipping. + +Confirm the account: `list_connected_accounts`. This applies to search platforms (**platform-google**, **platform-microsoft**). + +## 1. Start from intent, not volume + +Anchor to the conversion that defines success. Sort candidate terms by buyer intent: +- **High intent:** problem-aware, ready to act ("buy", "near me", branded competitor terms). +- **Research intent:** comparing, not yet buying. Lower priority unless the funnel supports nurture. +- **Junk:** informational or off-topic. These become negatives, not keywords. See the **negative-keywords** skill. + +## 2. Mine what already works + +Use `research_campaign_opportunity` and available `pull_google_ads_performance` or `pull_microsoft_ads_performance` history. Request a platform search-term export if query-level detail is missing. Separate observed converting queries from proposed keywords; do not invent query performance. + +## 3. Assign match types deliberately + +- **Exact** for proven converters you want control over. +- **Phrase** for controlled expansion. +- **Broad** only with strong negatives and conversion tracking, never naked. + +## 4. Add on approval + +Draft supported keyword entities with `upsert_plan_entity` and follow **launch** for review, preflight, approval, execution, and verification. Use the platform UI if keyword edits are unsupported. Pair expansions with exclusions and verify tracking with `ga4_get_conversions` and **conversion-tracking**. + +## Rules + +- Group by intent and theme so RSAs stay relevant; relevance is what earns cheaper clicks. +- Never add broad match without negatives and tracking. +- Report the terms added and the ad group they landed in with real IDs. diff --git a/packages/claude-directory/skills/kill-scale-rules/SKILL.md b/packages/claude-directory/skills/kill-scale-rules/SKILL.md new file mode 100644 index 0000000..f03d394 --- /dev/null +++ b/packages/claude-directory/skills/kill-scale-rules/SKILL.md @@ -0,0 +1,35 @@ +--- +name: kill-scale-rules +description: Decide whether to kill or scale a campaign, ad set, or creative, with minimum-data guardrails and platform-safe budget-step limits. Use when a user asks whether to pause, cut, kill, scale, or increase budget on something, or wants rules for when to do it. +--- + +# Kill or Scale + +The two decisions that move an account: cut what is losing, feed what is winning. Both fail when made too early or too hard. Reads do not require approval; pausing or scaling spends differently, so confirm before shipping. + +Confirm the account: `list_connected_accounts`. + +## Minimum data first + +Never kill or scale on noise. Require enough conversions and enough time for the platform to have learned. For a new ad set still in Meta's Learning Phase, or a Search campaign with a handful of clicks, the answer is usually "wait", not "cut". Run **pre-pause-analysis** before pausing anything. + +## When to kill + +- Real spend, enough conversions to judge, CPA well above target with no fixable cause (targeting, negatives, creative, tracking all checked). +- Creative fatigue past recovery (rising frequency, falling CTR) that rotation will not fix (see **creative-fatigue-detector**). +- Before you pause, confirm it is not a tracking or geo problem masquerading as poor performance. + +## When to scale + +- Consistent performance at or below target CPA/ROAS with volume, held across enough days. +- Room to grow (impression share lost to budget, or audience not saturated). + +## Scale in safe steps + +Raise budget in increments, not leaps. Large jumps reset learning and can spike CPM or CPA. Increase, hold, re-measure, increase again. Match the step to the platform and the ad set's stability. Use `update_campaign_budget` and confirm. + +## Rules + +- Minimum data before any kill or scale call. State the window and volume. +- Rule out tracking, geo, and creative before blaming the campaign. +- Report the decision, the evidence, and the step taken. Scaling is a separate, approved action every time (see **optimize**). diff --git a/packages/claude-directory/skills/landing-page-creation/SKILL.md b/packages/claude-directory/skills/landing-page-creation/SKILL.md new file mode 100644 index 0000000..42c4cec --- /dev/null +++ b/packages/claude-directory/skills/landing-page-creation/SKILL.md @@ -0,0 +1,25 @@ +--- +name: landing-page-creation +description: Prepare landing-page copy and an implementation brief using the advertiser's verified brand, offer, and conversion destination. This directory package does not publish websites. +--- + +# Prepare a landing page + +Confirm the workspace and advertiser using `list_connected_accounts` and the user's verified context. Read [brand and safety guidance](../../context/brand-and-safety.md). Use `list_creative_assets` to identify approved original assets. Establish the audience, offer, evidence, and intended conversion before drafting. + +Use the user's real form, calendar, or checkout destination. Never invent a destination or assume the advertiser uses Synter's own website. For Synter itself, use the verified canonical domain https://synterai.com. Identify the existing analytics and attribution requirements without claiming that a proposed form or tracking event is already installed. + +Draft the hero, offer explanation, supporting evidence, objection handling, and primary CTA as connected page copy. Include a mobile and desktop implementation brief with typography, exact asset references, section order, form destination, and accessibility requirements. Label missing proof or assets clearly. + +This directory connection has no website creation or publishing tool. If the host has a separately available, user-authorized site editor, inspect its capabilities and use that editor's preview and publishing workflow. Otherwise deliver the copy and implementation brief and state that no page has been published. Verify the actual rendered page, links, form submission, and tracking before reporting a page live. + +```mermaid +flowchart LR + A["Verify brand and destination"] --> B["Draft page copy"] + B --> C["Create implementation brief"] + C --> D["Available site editor"] + D --> E["Preview and verify"] + E --> F["Authorized publication"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E,F step; +``` diff --git a/packages/claude-directory/skills/launch/SKILL.md b/packages/claude-directory/skills/launch/SKILL.md new file mode 100644 index 0000000..83e25ce --- /dev/null +++ b/packages/claude-directory/skills/launch/SKILL.md @@ -0,0 +1,54 @@ +--- +name: launch +description: Plan and launch a cross-platform ad campaign — strategy, targeting, creative, budget, and preflight — then ship it on the user's approval. Use when a user wants to launch, build, or run a new campaign on one or more platforms (Google, Meta, LinkedIn, Reddit). +--- + +# Launch a Campaign + +Take the user from intent to a live campaign. Plan it, show it, ship it on their word. You direct nothing on your own — every spend-creating step waits for explicit approval. + +## 1. Scope it + +Get the brief in plain English, then pin down what you need: + +- **Objective** (leads, signups, sales, awareness) and the **conversion** that defines success. +- **Platform(s)** — recommend the mix; don't just take the first one named. +- **Audience** — who, where. If it needs building, run the **audience** skill first. +- **Budget** — daily/total, and the **target CPA/ROAS**. +- **Landing page / final URL** and the offer. + +Confirm the account with `list_connected_accounts`. Check conversion evidence with `ga4_get_conversions`, `ga4_get_report`, and `get_pixel_health`. Resolve missing verification before launching. + +## 2. Plan it + +Use `create_campaign_plan` to draft structure (campaigns → ad sets → ads), add supported entities with `upsert_plan_entity`, then `forecast_campaign` for reach/CPM/CPC/CPA projections. For the channel mix and budget split, lean on platform cost benchmarks and a clear rationale. + +Write text with `create_ad_copy` and select existing approved assets with `list_creative_assets`. Use the verified advertiser’s voice, logos, colors, and destination; see `../../context/brand-and-safety.md`. + +## 3. Preflight — before anything goes live + +Read the complete plan with `get_campaign_plan` and run `run_launch_preflight`. Resolve failures, then show the exact final plan and report pass/fail: + +- Geo targeting correct, exclusion lists applied. +- Conversion tracking firing and attached to the right action. +- Budget and bid caps sane — guard against fat-finger budgets (a daily budget that's 10–100x intended is the classic incident). +- Creative passes platform policy (`ad-policy-compliance` thinking) and brand rules. +- Final URL resolves and matches the ad's promise. + +## 4. Ship on approval + +Show the full plan — platform, audience, budget, creative, projected CPA — and ask for a clear go. Only then: + +- Record the user’s approval with `approve_campaign_plan`, then use `execute_campaign_plan`. Check `get_plan_execution` and, if a job ID is returned, `get_job_status`. Use `enable_campaign` only when activation is within the user-approved scope. +- For multi-platform, ship each platform and confirm each one back with its real campaign ID. + +Then confirm live: `list_campaigns(platform=...)` and report the IDs created. "Created" and "live and spending" are different claims — verify before you say it's running. + +## House rules + +- Nothing that spends money ships without explicit approval. Default to recommend-then-execute. +- ALWAYS call `list_connected_accounts` or `get_connection_status` first to validate the target Account ID and Account Name on multi-account setups. +- Whenever reporting back, you MUST explicitly include the **Platform**, **Account Name**, and **Account ID** (e.g. `Google Ads: Acme Corp (ID: 123-456-7890)`). +- Use real account IDs returned by the tools — never invent them. +- Start conservative on budget; scale winners later with the **optimize** skill. + diff --git a/packages/claude-directory/skills/linkedin-ads-targeting/SKILL.md b/packages/claude-directory/skills/linkedin-ads-targeting/SKILL.md new file mode 100644 index 0000000..f40cf2f --- /dev/null +++ b/packages/claude-directory/skills/linkedin-ads-targeting/SKILL.md @@ -0,0 +1,60 @@ +--- +name: linkedin-ads-targeting +description: Build LinkedIn Ads audiences with job title, seniority, and company targeting, ABM company lists, lookalikes, and lead gen form optimization. Use when building LinkedIn audiences, setting up ABM, choosing ad formats, optimizing lead gen forms, or estimating LinkedIn costs. +--- + +# LinkedIn Ads Targeting + +LinkedIn is the most expensive click in B2B; it only pays when the audience is layered tightly enough that every impression is a plausible buyer. + +Confirm the account: `list_connected_accounts`. + +## 1. Build the audience in layers + +Layer categories with AND logic, options within a category with OR: + +- **Job function or title:** e.g. Marketing OR Sales. Titles are text — add variations. +- **Seniority:** Director OR VP OR CXO for buying-committee reach. Skip individual contributors unless they are the actual users driving adoption. +- **Company size:** pick the employee ranges matching your ICP (e.g. 201-5000 for mid-market). +- **Industry:** map your vertical to LinkedIn's taxonomy. +- **Geography:** country or region. + +Size targets: below ~50,000 members delivery suffers for Sponsored Content; 100,000-300,000 is the sweet spot; above ~500,000 you are paying LinkedIn CPMs for non-ICP reach — narrow with skills or a company list. Under ~10,000 is workable only for ABM and message formats. + +Review existing audiences with `list_audiences`. Define company, seniority, and job criteria; have the account owner create and synchronize new lists in the platform UI. See **audience** for the planning workflow. + +## 2. Set up ABM + +1. Prepare the company criteria and required domains, then have the account owner create the list in the platform UI. Confirm returned audience IDs and observed match rates with `list_audiences`; do not promise a match rate. +2. Layer the account list with seniority (Director+) and the job functions that own the buying decision. +3. Tier the accounts: Tier 1 (top strategic accounts) gets highest bids and personalized messaging, Tier 2 industry-specific messaging, Tier 3 broader value prop at lower bids. +4. Use `pull_linkedin_ads_performance` for available campaign metrics and **attribution** for pipeline analysis. Request exports for company-level engagement not returned by the tools. + +## 3. Choose the format + +- **Awareness:** Sponsored Content single image or video, broad audience (100k+). +- **Consideration:** carousel or video, Conversation Ads for multi-CTA journeys, mid-size audience. +- **Lead gen:** Sponsored Content or Message Ads with Lead Gen Forms, tight audience (50k-150k). +- **Cheap presence:** Text Ads in the sidebar — low CPC, low volume. + +Compare CPC, CPM, CPL, and CTR with the account’s recent history for the same objective and audience. Use **media-plan** for the proposed test budget and label estimates clearly. + +## 4. Optimize lead gen forms + +- Fewer fields, higher conversion: 3 pre-filled fields converts roughly 12-15%, 5 fields 8-12%, 7+ fields drops to 4-8%. Phone number alone cuts conversion 20-30%. +- Stick to pre-filled fields (name, email, company, title) plus at most one custom qualifying question. +- Headline states what they get ("Get the ROI Report"), CTA button matches ("Download Now"), thank-you message sets next-step expectations. +- Protect lead quality with targeting (seniority, company size) and one qualifying question rather than more form fields. +- Speed-to-lead matters: sync leads to CRM fast and follow up within 24 hours. + +## 5. Verify and iterate + +Confirm campaign state with `list_campaigns` and delivery with `pull_linkedin_ads_performance`. Configure alerts and new expansion audiences in the platform UI. Use **ad-copy-generation** for text and **platform-linkedin** for campaign planning. + +## Rules + +- Every audience gets a seniority layer. Unlayered function targeting wastes most of the spend. +- Check estimated audience size before launch; fix anything under 50k (non-ABM) or over 500k. +- ABM lists need domains, not just company names, for good match rates. +- Judge lead gen on qualified leads and pipeline, not form fills. +- Judge LinkedIn costs against lead quality, pipeline value, and the account’s own benchmarks before proposing a pause. diff --git a/packages/claude-directory/skills/llm-visibility-audit/SKILL.md b/packages/claude-directory/skills/llm-visibility-audit/SKILL.md new file mode 100644 index 0000000..351fb73 --- /dev/null +++ b/packages/claude-directory/skills/llm-visibility-audit/SKILL.md @@ -0,0 +1,22 @@ +--- +name: llm-visibility-audit +description: Assess brand discoverability using supplied AI-search results, website content, and crawl evidence. Distinguish observed citations from untested recommendations. +--- + +# Audit AI search visibility + +Identify the verified brand, canonical domain, target customers, and buying questions. This directory connection has no AI-search visibility measurement tool. Use evidence the user supplies, or a separately available research tool when authorized. Record the exact query, engine, date, source URL, and observed citation for each measured result. Never invent a visibility score or treat a page change as proof of ranking. + +Review the website's clarity about the company, product, pricing, and supported claims. Where actual page or crawl evidence is available, check robots directives, accessible documentation, and structured data for accuracy. A crawler allowance or an index file does not establish that an AI service will ingest or cite the site. Respect the owner's crawl preferences; do not recommend removing restrictions without understanding their purpose. + +Separate the report into observations supported by evidence, missing measurements, and proposed fixes. Prioritize incorrect entity details, inaccessible relevant content, and unsupported claims. Make factual recommendations specific to the page or query and link the evidence. Re-run the same dated queries after changes before describing an improvement. + +```mermaid +flowchart LR + A["Define brand and queries"] --> B["Collect dated evidence"] + B --> C["Separate findings from gaps"] + C --> D["Propose content fixes"] + D --> E["Repeat measurements"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E step; +``` diff --git a/packages/claude-directory/skills/media-plan/SKILL.md b/packages/claude-directory/skills/media-plan/SKILL.md new file mode 100644 index 0000000..0776381 --- /dev/null +++ b/packages/claude-directory/skills/media-plan/SKILL.md @@ -0,0 +1,36 @@ +--- +name: media-plan +description: Build a full media plan — objective, channel mix, budget split, projected CPM/CPC/CPA, and a first test flight — before any campaign is built. Use when a user wants a media plan, a budget allocation across platforms, or a go-to-market for a launch. +--- + +# Media Plan Builder + +Turn a goal and a budget into a defensible plan across platforms, with the math shown, before anything is built. Planning only; building and spending are the **launch** skill's job and wait for approval. + +Confirm what is available first: `list_connected_accounts`. + +## 1. Start from the objective + +Anchor to the conversion that defines success and the target CPA or ROAS. Get the budget, audience, geo, and offer. Do not start from a platform. + +## 2. Choose the channel mix + +Ground channel weights in account history using `pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, and `pull_reddit_ads_performance`. Use `research_campaign_opportunity` for available research and `forecast_campaign` for estimates; label projections clearly. Balance demand capture and demand generation for the objective. + +## 3. Split the budget and project outcomes + +Allocate budget per channel with projected CPM/CPC/CPA/reach from `forecast_campaign`. Show the math; a split without projected outcomes is a guess. Start conservative and name the scale path. + +## 4. Plan measurement and a first flight + +State which conversion, which tracking (see **conversion-tracking**), and how success is judged. Define a first two-week test flight with a clear read before wider spend. + +## Output + +Objective → audience → channel mix and budget split → projected CPA/ROAS per channel → creative direction → measurement → first-flight test. Lead with the recommendation, then the reasoning. Hand off to **launch** to build and ship on approval. + +## Rules + +- Show the budget math and projected outcomes per channel; no unsupported splits. +- Prefer the account's own history over any benchmark, and never present a projection as a guarantee. +- Include measurement in the plan; a plan you cannot read is not a plan. diff --git a/packages/claude-directory/skills/meta-ads-diagnostics/SKILL.md b/packages/claude-directory/skills/meta-ads-diagnostics/SKILL.md new file mode 100644 index 0000000..a82bbbb --- /dev/null +++ b/packages/claude-directory/skills/meta-ads-diagnostics/SKILL.md @@ -0,0 +1,59 @@ +--- +name: meta-ads-diagnostics +description: Diagnose Meta Ads delivery and performance problems — Learning Phase stalls, creative fatigue, audience overlap, CPM spikes, and Advantage+ vs manual structure. Use when a Meta campaign underperforms, delivery is limited, or CPMs and frequency are climbing. +--- + +# Meta Ads Diagnostics + +Find the real cause before touching the campaign. Most Meta "performance problems" are one of four things: learning starvation, creative fatigue, audience self-competition, or a post-click issue. + +Confirm the account: `list_connected_accounts`, then pull data with `pull_meta_ads_performance(days=30, level="ad-set")` and at ad level for creative checks. + +## 1. Check Learning Phase first + +Meta needs roughly 50 optimization events per ad set per week to stabilize. Under that, CPA runs volatile and high, and no other diagnosis is reliable. + +- Learning resets on: budget moves over ~20%, bid changes, new creatives, targeting edits, optimization-event changes, or a pause longer than 7 days. +- Stuck in Learning Limited: consolidate ad sets so events pool, broaden the audience, raise budget toward 10x target CPA, or optimize to a higher-volume event (Add to Cart instead of Purchase). +- If it launched or changed within the last 7 days: do not touch it. Wait. + +## 2. Test for creative fatigue + +Pull daily impressions, reach, frequency, and CTR for the last 30 days. Compute fatigue as the CTR decline from the first full week to now: + +- Under 15% decline: healthy. +- 15-30%: early fatigue, prepare a refresh. +- 30-50%: rotate creatives now. +- Over 50%: pause and replace. + +Frequency confirms it: prospecting past ~3.0 or retargeting past ~5.0 with falling CTR is fatigue, not audience quality. Refresh in proportion — copy swap for mild decline, new visual angle for moderate, new concept or format change for severe. Build replacements with the **ad-copy-generation** skill and validate with **creative-testing**. + +## 3. Check audience overlap + +Symptoms: CPMs rising on multiple ad sets at once, frequency climbing faster than spend justifies, two ad sets going Learning Limited together, one ad set starving the other. + +- Under ~10% overlap: ignore. 10-25%: watch CPMs. Over 25%: exclude one audience from the other or merge the ad sets. Over 50%: consolidate immediately. +- Consolidation into fewer, broader ad sets usually beats exclusion lists — it also fixes learning starvation at the same time. + +## 4. Respect the breakdown effect + +Per-placement or per-demo breakdowns lie. Meta optimizes holistically, so a placement with an ugly standalone CPA may be feeding cheap assists to the placements that convert. Never cut a placement off breakdown data alone — measure it properly with `measure_incrementality` or leave it on. Give the algorithm 14+ days with all placements before judging. + +## 5. Advantage+ vs manual structure + +- Advantage+ Shopping fits: product catalog, 100+ conversions/week, broad targeting, simple purchase goals. Set an existing-customer budget cap (20-30%) and feed it 5-10 diverse creatives. +- Manual fits: B2B lead gen, narrow audiences, heavy exclusions, under ~50 conversions/week, or when you need creative-level budget control. +- When testing ASC against manual, run both at equal budget for 14+ days and compare CPA, ROAS, and new-customer share before consolidating. + +## 6. Decision tree for the rest + +Diagnose in order: delivery status, frequency and fatigue, auction pressure, landing-page conversion, then tracking. Use `get_pixel_health` and **conversion-tracking** for available tracking evidence. + +Apply approved pauses or budget changes with `pause_campaign` or `update_campaign_budget`. Select existing replacements with `list_creative_assets` and follow **launch** for supported plan changes. Kill/scale decisions use **kill-scale-rules**. + +## Rules + +- Never diagnose a campaign still in Learning Phase; fix event volume first. +- One change at a time — every edit can reset learning. +- Breakdown data is directional, not causal. Incrementality beats breakdowns. +- State the data window and event counts behind every recommendation. diff --git a/packages/claude-directory/skills/mmm-budget-planner/SKILL.md b/packages/claude-directory/skills/mmm-budget-planner/SKILL.md new file mode 100644 index 0000000..6ef03ec --- /dev/null +++ b/packages/claude-directory/skills/mmm-budget-planner/SKILL.md @@ -0,0 +1,39 @@ +--- +name: mmm-budget-planner +description: Plan and reallocate budget across platforms by ROI, with revenue projections and shift scenarios. Use when a user asks where to put budget across channels, the true ROI of each channel, what happens if they move spend from one platform to another, or how campaigns are pacing. +--- + +# Budget Planner + +Decide where the next dollar goes across platforms, grounded in real performance rather than gut feel. Reads do not require approval; changing budgets is a change and waits for explicit approval. + +Confirm the account and what is connected: `list_connected_accounts`. + +## 1. Read cross-platform performance + +Pull recent performance per platform so the plan sits on real numbers: + +- Use `pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, and `pull_reddit_ads_performance` for available spend, conversions, and CPA by channel. +- `get_attribution` to see how conversions credit across touchpoints, not just last click. +- `measure_incrementality` where a holdout exists, to separate true lift from spend that would have converted anyway. This is the honest answer to "what is this channel really worth." + +## 2. Reallocate by ROI + +Use `optimize_budget` for a proposed allocation within the user’s total and per-channel limits supported by its schema. Treat the result as a recommendation until the exact changes are approved and applied. + +Read the output as a plan, not a command: + +- Every channel has diminishing returns. Pouring the whole budget into the current best performer stops working once it saturates. Keep a spread. +- Compare the proposed allocation to the current one and show the delta and the expected improvement. +- Model shifts explicitly: "move X from platform A to platform B" and what the projected CPA/return does. Keep the math visible. + +## 3. Project and pace + +- Use recent trend plus the proposed allocation to project the next few weeks of spend and conversions, with a clear range rather than a false-precision single number. +- Flag channels pacing over or under budget so nothing quietly runs away or underspends. + +## 4. Ship on approval + +Show the current split, proposed split, projected outcome, and rationale. Change budgets only on explicit approval with `update_campaign_budget`, or follow **launch** for an approved plan. Guard against mistaken amounts. Confirm the new settings with `list_campaigns` and monitor subsequent performance. + +For single-account bid and pacing tuning rather than cross-platform allocation, use the **optimize** and **bid-optimization** skills. For pure ROAS and CPA math, use the **roas-calculator** skill. diff --git a/packages/claude-directory/skills/negative-keywords/SKILL.md b/packages/claude-directory/skills/negative-keywords/SKILL.md new file mode 100644 index 0000000..c91001a --- /dev/null +++ b/packages/claude-directory/skills/negative-keywords/SKILL.md @@ -0,0 +1,35 @@ +--- +name: negative-keywords +description: Mine and manage negative keywords from search-term reports to stop wasted spend, and detect match-type bleed and cross-campaign conflicts. Use when a user wants to cut wasted spend, clean up search terms, build negative lists, or fix irrelevant clicks on Google or Microsoft Ads. +--- + +# Negative Keywords + +Negatives are how a Search campaign stops paying for the wrong clicks. They matter as much as the keywords themselves. Reads do not require approval; adding negatives is a change, so confirm before shipping. + +Confirm the account: `list_connected_accounts`. + +## 1. Read the search-term report + +Use `research_campaign_opportunity` and available `pull_google_ads_performance` or `pull_microsoft_ads_performance` history. Request a platform search-term export if query-level detail is missing. Separate observed converting queries from proposed keywords; do not invent query performance. + +## 2. Categorize + +- **Wasteful:** spend, no conversions, clearly off-topic. Add as negatives now. +- **Ambiguous:** some signal, unclear intent. Watch before cutting. +- **Bleed:** broad or phrase keywords matching queries they should not. Tighten match types or add negatives to contain them. +- **Cross-campaign conflict:** the same query served by two campaigns competing against each other. Resolve with campaign-level negatives so each campaign owns its intent. + +## 3. Choose the level + +Add negatives at ad-group level for surgical fixes, campaign level for account-wide junk, and use shared negative lists for terms every campaign should exclude. + +## 4. Add on approval + +Apply negatives through the plan tools and confirm. Re-pull the search-term report after a few days to verify the waste stopped. + +## Rules + +- Pair every keyword expansion (see **keyword-research**) with negatives; expansion without negatives leaks budget. +- Do not negate a term that has converted, even once, without checking the numbers. +- Report the negatives added, the level, and the spend they were burning. diff --git a/packages/claude-directory/skills/optimize/SKILL.md b/packages/claude-directory/skills/optimize/SKILL.md new file mode 100644 index 0000000..4b11e42 --- /dev/null +++ b/packages/claude-directory/skills/optimize/SKILL.md @@ -0,0 +1,45 @@ +--- +name: optimize +description: Improve campaign performance — reallocate budget by ROAS, recommend bid changes for manual application, kill losers and scale winners, and catch anomalies across platforms. Use when a user wants to cut wasted spend, improve ROAS/CPA, rebalance budget, or act on what's underperforming. +--- + +# Tune & Reallocate + +Find the wasted spend and the winners, then act on the user's approval. + +## 1. Pull the truth + +Get recent performance across the connected platforms: +Use `pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_reddit_ads_performance`, and `pull_microsoft_ads_performance`. Request exports for other platforms. Use `get_spend_reconciliation` to compare sources and `get_attribution` to investigate conversion credit. + +## 2. Diagnose + +- **Wasted spend**: campaigns/ad sets/creatives spending with no conversions past a minimum-data threshold. +- **Winners**: low CPA / high ROAS with enough volume to scale. +- **Anomalies**: spend spikes, CTR drops, budget runaway — flag before they compound. +- **Fatigue**: rising frequency with falling CTR → creative needs rotation (run the **ad-copy-generation** skill). + +Respect minimum-data guardrails — don't kill or scale on noise. State the threshold you used. + +## 3. Recommend, then act + +Present a clear plan: what to cut, what to scale, where the freed budget goes, and the expected effect. + +- `optimize_budget` — propose a cross-platform allocation for review. +- `update_campaign_budget` — set a specific budget (guard against fat-finger amounts; default to sane ceilings). +- `pause_campaign` — kill a loser. +- Bid-strategy changes: recommend them and hand off to the platform UI. + +Scale winners in steps (platform-appropriate budget increments), not all at once — big jumps reset the learning phase. + +## 4. Confirm + +After acting, re-pull the data and report the new state. Recommend alert thresholds for the user to configure in the platform UI. + +## House rules + +- Always validate connected Account IDs (`get_connection_status`) first. Explicitly state the **Platform**, **Account Name**, and **Account ID** (e.g. `Google Ads: Acme Corp (ID: 123-456-7890)`) when reporting optimizations and state changes. +- Every budget/pause/bid change needs explicit approval. Recommend-then-execute by default. +- Minimum-data guardrails before any kill-or-scale call. +- Never report a change as done without confirming it landed. + diff --git a/packages/claude-directory/skills/performance-max-optimizer/SKILL.md b/packages/claude-directory/skills/performance-max-optimizer/SKILL.md new file mode 100644 index 0000000..0c32a70 --- /dev/null +++ b/packages/claude-directory/skills/performance-max-optimizer/SKILL.md @@ -0,0 +1,62 @@ +--- +name: performance-max-optimizer +description: Optimize Google Ads Performance Max — asset group structure, asset performance labels, audience signals, search term insights, negatives, and channel allocation. Use when managing or auditing PMax campaigns, replacing Low assets, or diagnosing where PMax spend actually goes. +--- + +# Performance Max Optimizer + +PMax gives up query and placement control, so you steer it with the three inputs you still own: assets, audience signals, and the feed. Optimize those; don't fight the black box. + +Confirm the account with `list_connected_accounts`, then use `pull_google_ads_performance` and `audit_account_structure`. Request platform exports when asset or query-level details are missing. + +## 1. Fill every asset slot + +Inventory approved assets with `list_creative_assets` and write missing text with `create_ad_copy`. Request any missing images, logos, or video from the advertiser. Review asset completeness and brand consistency before following **launch**. + +## 2. Replace Low assets on a cycle + +Review asset performance labels in a Google Ads export if they are absent from the tool results. Do not infer asset-level labels from campaign performance. + +- BEST: keep, and spin 2-3 close variations off it. +- GOOD: keep and monitor. +- LOW: replace within 2-4 weeks with variants of your BEST assets. +- LEARNING / PENDING: wait 2+ weeks before judging. + +Replace 1-2 assets at a time and never wipe a whole asset type at once. Validate concepts with **creative-testing** before rolling them across groups. + +## 3. Mine search term insights and cut waste + +Review a platform search-category export for irrelevant or unprofitable traffic. Propose negatives with **negative-keywords**, verify current platform support, and have the account owner apply unsupported changes in the platform UI. + +The most expensive miss: brand queries. If insights show a large brand share, PMax is buying conversions your brand campaign gets cheaper — add brand terms as account-level negatives and let the brand campaign recover. + +## 4. Feed audience signals, not audience targets + +Signals guide initial learning; PMax expands beyond them. Stack them by strength: + +- Review customer and high-value purchaser lists with `list_audiences`. Have the account owner create or refresh lists in the platform UI before using them in supported campaign-plan fields. +- Custom segments from high-intent search terms and competitor URLs (50-100 entries each). +- Site visitors and cart abandoners from GA4 (`ga4_get_report` to size them). +- Interests and demographics last, as seasoning only. + +After 30 days, check which segments convert and whether expansion aligns with them. Deeper audience work lives in **audience**. + +## 5. Check channel allocation + +Use a platform channel export when the performance response lacks channel breakdowns. Compare conversion value and spend by channel before recommending a reallocation: + +- Display bloated with weak CVR: audience signals too loose — add purchase-intent custom segments. +- Search share mostly brand: negate brand at account level. +- Shopping share low for a catalog business: feed quality problem — see **google-shopping-optimizer**. +- Zero YouTube: you have no video asset; add one. + +## 6. Structure asset groups by intent + +One asset group per coherent product/audience theme, each with its own listing group filter and matched signals — not one mega-group. Use custom labels on the feed to split hero, seasonal, and long-tail products across groups. Budget and target changes go through `update_campaign_budget` and **bid-optimization**; give the campaign 2+ weeks after any target move. + +## Rules + +- Never judge PMax in its first 2-3 weeks or right after a target/budget change. +- Brand traffic makes PMax look better than it is — always separate brand before reading ROAS. +- Change one input class per cycle (assets, signals, negatives, or targets), then wait. +- Report channel mix and asset-label distribution, not just topline ROAS. diff --git a/packages/claude-directory/skills/pixel-capi-auditor/SKILL.md b/packages/claude-directory/skills/pixel-capi-auditor/SKILL.md new file mode 100644 index 0000000..369f739 --- /dev/null +++ b/packages/claude-directory/skills/pixel-capi-auditor/SKILL.md @@ -0,0 +1,58 @@ +--- +name: pixel-capi-auditor +description: Audit client-side pixels and server-side Conversions API implementations across Meta, Google, TikTok, LinkedIn, Reddit, and Microsoft — setup, Event Match Quality, deduplication, and click-ID capture. Use when conversion counts disagree between platforms, EMQ or match rates are low, or tracking needs verification before scaling spend. +--- + +# Pixel & CAPI Auditor + +Bad tracking corrupts every downstream decision, so audit it before optimizing anything. The audit is the same on every platform: is the pixel firing, is the server event arriving, are the two deduplicated, and is enough matched user data attached. + +Confirm the account with `list_connected_accounts` and `get_connection_status`. Inspect `get_pixel_health`; obtain platform diagnostics or tag-manager exports for missing implementation details. + +## 1. Verify the client side + +Per platform, confirm the base tag is present and fires on every page, standard events fire on the actions that matter (Purchase, Lead, AddToCart), and the platform click ID is captured from the landing URL into a first-party cookie: + +| Platform | Click ID param | Tag marker | +|---|---|---| +| Meta | `fbclid` (`_fbc`/`_fbp` cookies) | `fbq('init', ...)` | +| Google | `gclid` | Google tag + Conversion Linker | +| TikTok | `ttclid` | `ttq.load(...)` | +| LinkedIn | `li_fat_id` | Insight Tag partner ID | +| Reddit | `rdt_cid` (`_rdt_uuid`) | `rdt('init', ...)` | +| Microsoft | `msclkid` | UET tag, auto-tagging on | + +Verify ownership and event delivery in the platform UI when not evidenced by `get_pixel_health`. Have the account owner fix tags and publish through their tag manager, then repeat the test. + +## 2. Verify the server side + +Check server-event arrivals and routing in each platform’s diagnostics alongside `get_pixel_health`. The account owner must apply routing changes in the platform UI; this plugin does not configure destinations. + +## 3. Audit deduplication + +When pixel and CAPI both send an event, the platform deduplicates on a shared event ID (`event_id` on Meta/TikTok, `eventId` on LinkedIn, `conversion_id` on Reddit, `order_id`/`transaction_id` on Google). The rule: generate one ID server-side at conversion time (use the transaction ID) and pass the identical string to both the browser tag and the server event. + +Failure signatures: + +- Conversions roughly double the true count: event ID missing on one side. +- Counts too low: IDs present but formatted differently on each side, or event names mismatched. +- Platform reports far more conversions than GA4 shows (`ga4_get_report` on the conversion event): dedup broken — this is the most common finding. + +## 4. Raise match quality + +Match quality (Meta EMQ 0-10; TikTok match rate) decides how many conversions attribute at all. Send, in order of impact: hashed email (SHA-256 of lowercased, trimmed value), the platform click ID, hashed phone, browser cookie IDs, external ID, then client IP and user agent. Meta EMQ of 6+ is healthy; below 5 you are losing a meaningful share of attribution and the platform is optimizing on thin data. Adding hashed email and click ID alone typically moves EMQ several points. + +## 5. Reconcile across platforms + +Platforms can claim overlapping conversions. Compare platform results with `ga4_get_report` and inspect attribution with `get_attribution`, using consistent periods and stated attribution windows. Use `get_spend_reconciliation` for spend discrepancies; it does not establish conversion deduplication. + +## 6. Report and fix in priority order + +Produce a per-platform grid: pixel present, server events observed, dedup verified, match quality, and action needed. Mark unavailable evidence as unverified. Prioritize broken dedup and missing events on high-spend accounts; ask the owner to fix the implementation and repeat a test conversion before scaling. See **conversion-tracking**. + +## Rules + +- Never audit from code alone — confirm events actually arrive in each platform's diagnostics. +- One event ID, generated server-side, shared verbatim by pixel and CAPI. No exceptions. +- Hash PII (lowercase, trim, SHA-256) before it leaves the server; send user IP and user agent from the client, never the server's. +- Report discrepancies as percentages against GA4, and state which number you consider canonical. diff --git a/packages/claude-directory/skills/platform-amazon/SKILL.md b/packages/claude-directory/skills/platform-amazon/SKILL.md new file mode 100644 index 0000000..1048fe4 --- /dev/null +++ b/packages/claude-directory/skills/platform-amazon/SKILL.md @@ -0,0 +1,33 @@ +--- +name: platform-amazon +description: Amazon Ads and Amazon DSP playbook — Sponsored Products/Brands/Display, ACOS/TACOS, retail readiness, and programmatic. Use when planning, reading, auditing, or launching Amazon Ads or DSP, or porting a campaign to Amazon. +--- + +# Amazon Ads and DSP + +Two motions: Sponsored ads on the retail search results, and DSP for programmatic reach on and off Amazon. Retail readiness gates everything: an ad on a weak product page burns budget. + +## Read what exists + +- `list_connected_accounts`, then `list_campaigns(platform="amazon")`. +- Dedicated performance reads for this platform are outside the plugin’s directory tools. Request a platform export for metrics and use `list_campaigns` for the state and fields actually returned. Do not substitute another platform’s data. + +## Sponsored ads + +- **Sponsored Products** for individual listings, **Sponsored Brands** for brand and portfolio, **Sponsored Display** for on- and off-Amazon retargeting. +- The core metric is **ACOS** (ad spend / ad sales); watch **TACOS** (ad spend / total sales) to see whether ads are growing the whole business or just shifting organic sales. +- Mine search terms, keep negatives tight, and structure by product intent. Do not scale a campaign pointing at a page with thin content, bad images, or no reviews. + +## Amazon DSP + +- Review existing audiences with `list_audiences`. Use `research_campaign_opportunity` for available campaign research. Ask the account owner to create or synchronize new audience lists in the platform UI; use returned IDs only in supported plan fields. + +## Plan and ship + +- Build with `create_campaign_plan` + `upsert_plan_entity`; `forecast_campaign` for projections. Run **campaign-preflight**: retail readiness, tracking, sane budgets. +- Ship only on explicit approval: `execute_campaign_plan`, then `enable_campaign`. Nothing that spends money goes live without a clear go. Apply brand voice from `../../context/brand-and-safety.md`. +- Confirm live with `list_campaigns(platform="amazon")` and report the real ID. Scale by ACOS with the **optimize** skill. + +Porting from another platform? Use the **replicate** skill. Search intent maps to Sponsored Products keywords; audiences map to DSP. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-benchmarks/SKILL.md b/packages/claude-directory/skills/platform-benchmarks/SKILL.md new file mode 100644 index 0000000..ec5963e --- /dev/null +++ b/packages/claude-directory/skills/platform-benchmarks/SKILL.md @@ -0,0 +1,33 @@ +--- +name: platform-benchmarks +description: Ground channel-mix and budget-split decisions in typical CPM, CPC, and CPA ranges per platform, and set realistic performance expectations. Use when a user asks what to expect on a platform, how to split budget across channels, or whether their numbers are normal. +--- + +# Platform Benchmarks + +Set expectations and split budget using how each platform typically performs for an objective and audience. Use this to sanity-check numbers and inform a media plan, not as a promise; the account's own history beats any benchmark. + +## Use the account's own data first + +Before quoting a range, pull what this account already does: the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`) and `forecast_campaign`. Real history for this advertiser is the truth. Benchmarks are for platforms not yet run or for a directional split. + +## How platforms typically behave + +- **Search (Google, Microsoft):** high intent, higher CPC, strong conversion rate. Best for demand capture. Microsoft usually cheaper clicks, lower volume than Google. +- **Meta (Facebook/Instagram):** low CPM, broad reach, demand generation. Creative-driven; performance swings with the creative. +- **TikTok:** low CPM, young skew, creative-first and fast-fatiguing. +- **LinkedIn:** high CPC and CPL, precise B2B targeting. Judge on pipeline quality, not click cost. +- **Reddit, X:** cheaper clicks that can be a false economy. Validate conversions, not just clicks. +- **Amazon:** measured on ACOS/TACOS, closest to the purchase. + +## Splitting budget + +Weight toward demand capture (search) for immediate conversions and demand generation (social) for growth, sized to the objective and the funnel. Show the split with a rationale and projected CPA per channel. Hand off to the **media-plan** skill to formalize it and **launch** to ship. + +## Rules + +- Never present a benchmark as a guaranteed result. State it as a typical range and name the source of any real number. +- Prefer the account's own history over any external figure. +- If you do not have a real number, say so; a plain true line beats an invented impressive one. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-google/SKILL.md b/packages/claude-directory/skills/platform-google/SKILL.md new file mode 100644 index 0000000..3d534f5 --- /dev/null +++ b/packages/claude-directory/skills/platform-google/SKILL.md @@ -0,0 +1,36 @@ +--- +name: platform-google +description: Operate Google Ads through Synter — read and build Search, Performance Max, Display, Demand Gen, and Video campaigns, with the right structure, match types, negatives, geo, and bid strategy. Use when a user is working on Google Ads, asks about Search/PMax/Shopping, or wants to read or change a Google campaign. +--- + +# Google Ads Playbook + +How Google Ads is shaped and how to operate it through Synter. Reads do not require approval; anything that spends money waits for explicit approval. + +Confirm the account first: `list_connected_accounts`. Use the real customer ID the tool returns; never invent one. Not connected → run the **connect** skill. + +## Structure + +- **Search:** campaign → ad group → keywords (with match types: exact, phrase, broad) → responsive search ads (RSAs, up to 15 headlines / 4 descriptions, some pinned). Negatives live at campaign and ad-group level and matter as much as the keywords. +- **Performance Max:** campaign → asset groups + audience signals. No keywords; goals and conversion data drive it. +- **Display / Demand Gen / Video:** campaign → ad group → audiences + creative. Video runs on YouTube. +- Campaign settings that must be right: budget, bid strategy, networks, geo/location criteria, languages, ad schedule. + +## Read a campaign + +Use `list_campaigns` for returned campaign IDs and settings, `audit_account_structure` for available structure checks, and `pull_google_ads_performance` for performance. Request a Google Ads export for detailed targeting, keyword match types, negatives, or asset data that the tools do not return. + +## Build a campaign + +Use `create_campaign_plan` and `upsert_plan_entity` for the supported campaign structure, and `forecast_campaign` for available estimates. Write text with `create_ad_copy` and select existing advertiser assets with `list_creative_assets`. Follow **launch** for `run_launch_preflight`, explicit user approval, `approve_campaign_plan`, `execute_campaign_plan`, and verification with `get_plan_execution` before any approved activation. + +## Operating rules + +- Keep match types deliberate. Broad without good negatives and conversion tracking wastes budget fast. +- Geo is the classic miss: a "US" campaign with no location criteria serves everywhere. Verify location criteria exist and match intent. +- Conversion tracking must exist and be attached before scaling: `ga4_get_conversions`, `get_pixel_health`. Blind spend is worse than no spend. +- Guard budgets against a fat-finger daily amount 10 to 100x intended. +- Adjust budgets with `update_campaign_budget`; pause with `pause_campaign`. Start conservative; scale winners with the **optimize** skill. +- "Created" is not "live and spending." Confirm with `list_campaigns` and report the real IDs. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-linkedin/SKILL.md b/packages/claude-directory/skills/platform-linkedin/SKILL.md new file mode 100644 index 0000000..58be634 --- /dev/null +++ b/packages/claude-directory/skills/platform-linkedin/SKILL.md @@ -0,0 +1,36 @@ +--- +name: platform-linkedin +description: Operate LinkedIn Ads through Synter — read and build B2B campaigns with job-title, company, and ABM targeting, the right ad format, and realistic CPL expectations. Use when a user is working on LinkedIn ads, B2B lead gen, or account-based targeting. +--- + +# LinkedIn Ads Playbook + +LinkedIn is the B2B platform: precise professional targeting, higher costs, longer consideration. How it is shaped and how to operate it through Synter. Reads do not require approval; anything that spends money waits for explicit approval. + +Confirm the account first: `list_connected_accounts`. Use the real ID the tool returns; never invent one. Not connected → run the **connect** skill. + +## Structure + +Campaign group → campaign (objective, audience, budget, schedule) → ad. Targeting is the core lever: job title and function, seniority, company, company size, industry, and skills. Keep audiences tight enough to be relevant but large enough to deliver. + +- Review existing audiences with `list_audiences`. Use `research_campaign_opportunity` for available campaign research. Ask the account owner to create or synchronize new audience lists in the platform UI; use returned IDs only in supported plan fields. +- **Formats:** single-image, document, video, and conversation/message formats. Match the format to the funnel stage. + +## Read a campaign + +Use `list_campaigns` for IDs and state and `pull_linkedin_ads_performance` for campaign metrics. Request a platform export for company-level engagement if needed. + +## Build a campaign + +Use `create_campaign_plan` and `upsert_plan_entity` for the supported campaign structure, and `forecast_campaign` for available estimates. Write text with `create_ad_copy` and select existing advertiser assets with `list_creative_assets`. Follow **launch** for `run_launch_preflight`, explicit user approval, `approve_campaign_plan`, `execute_campaign_plan`, and verification with `get_plan_execution` before any approved activation. + +## Operating rules + +- Expect higher CPL than other platforms. Judge LinkedIn on pipeline quality and downstream conversion, not raw CPC. +- Keep targeting to decision makers where the offer warrants it; do not spray a broad seniority range on an expensive platform. +- Give campaigns room before judging; B2B conversion windows are long. Follow **pre-pause-analysis** before cutting. +- Conversion tracking (Insight Tag and offline/CRM conversions) must be attached before scaling: `get_pixel_health`, `ga4_get_conversions`. Measure with `get_attribution`. +- Guard against a fat-finger daily budget 10 to 100x intended. Adjust with `update_campaign_budget`, pause with `pause_campaign`. +- "Created" is not "live and spending." Confirm with `list_campaigns` and report the real IDs. Build an audience first with the **audience** skill when one does not exist yet. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-meta/SKILL.md b/packages/claude-directory/skills/platform-meta/SKILL.md new file mode 100644 index 0000000..875fe1f --- /dev/null +++ b/packages/claude-directory/skills/platform-meta/SKILL.md @@ -0,0 +1,37 @@ +--- +name: platform-meta +description: Operate Meta (Facebook and Instagram) Ads through Synter — read and build campaigns with the right objective, ad-set structure, audiences, and creative, and handle the Learning Phase and creative fatigue. Use when a user is working on Meta, Facebook, or Instagram ads. +--- + +# Meta (Facebook / Instagram) Ads Playbook + +How Meta is shaped and how to operate it through Synter. Reads do not require approval; anything that spends money waits for explicit approval. + +Confirm the account first: `list_connected_accounts`. Use the real ID the tool returns; never invent one. Not connected → run the **connect** skill. + +## Structure + +Campaign (objective) → ad set (audience, budget, placements, schedule) → ad (creative). The objective is chosen once at the campaign level and shapes everything below it, so pick it from the conversion that defines success, not by habit. + +- Review existing audiences with `list_audiences`. Use `research_campaign_opportunity` for available campaign research. Ask the account owner to create or synchronize new audience lists in the platform UI; use returned IDs only in supported plan fields. +- **Advantage+** shifts targeting and placement decisions to Meta. Use it when data volume supports it; keep enough signal flowing. +- **Placements:** Feed, Stories, and Reels. Creative should fit the placement, not be stretched into it. + +## Read a campaign + +`list_campaigns(platform="meta")` for the list and IDs. `pull_meta_ads_performance(days=..., level="campaign")` for top line, `level="ad-set"` for audience and budget detail, `level="ad"` for creative performance. + +## Build a campaign + +Use `create_campaign_plan` and `upsert_plan_entity` for the supported campaign structure, and `forecast_campaign` for available estimates. Write text with `create_ad_copy` and select existing advertiser assets with `list_creative_assets`. Follow **launch** for `run_launch_preflight`, explicit user approval, `approve_campaign_plan`, `execute_campaign_plan`, and verification with `get_plan_execution` before any approved activation. + +## Operating rules + +- **Learning Phase:** a new ad set needs enough conversions to exit learning. Do not judge or kill it early, and do not edit it constantly; each significant edit resets learning. See the **pre-pause-analysis** discipline before cutting anything. +- **Creative fatigue:** rising frequency with falling CTR means the creative is spent. Watch it with the **creative-fatigue-detector** skill and rotate before performance collapses. +- Keep audience overlap in check; ad sets competing against each other inflate CPM. +- Conversion tracking (pixel and CAPI) must be firing and attached: `get_pixel_health`, `ga4_get_conversions`. Blind spend is worse than no spend. +- Guard against a fat-finger daily budget 10 to 100x intended. Adjust with `update_campaign_budget`, pause with `pause_campaign`. Start conservative; scale winners with the **optimize** skill. +- "Created" is not "live and spending." Confirm with `list_campaigns` and report the real IDs. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-microsoft/SKILL.md b/packages/claude-directory/skills/platform-microsoft/SKILL.md new file mode 100644 index 0000000..6de4bb1 --- /dev/null +++ b/packages/claude-directory/skills/platform-microsoft/SKILL.md @@ -0,0 +1,43 @@ +--- +name: platform-microsoft +description: Operate Microsoft (Bing) Ads through Synter — read and build Search campaigns that mirror Google, with match types, negatives, geo, schedules, and budgets, plus the real Google-to-Microsoft differences. Use when a user is working on Microsoft/Bing Ads or wants to move a Google campaign onto Microsoft. +--- + +# Microsoft (Bing) Ads Playbook + +Microsoft Ads is close to a mirror of Google Ads, which is why porting between them is common. Reads do not require approval; anything that spends money waits for explicit approval. + +Confirm the account first: `list_connected_accounts`. Use the real ID the tool returns; never invent one. Not connected → run the **connect** skill. + +## Structure + +Campaign → ad group → keywords (exact, phrase, broad) → responsive search ads, with negatives at campaign and ad-group level, plus geo/location criteria, ad schedule, and budget. If you know Google Search, you know this shape. + +## Read a campaign + +`list_campaigns(platform="microsoft")` for the list and IDs. `pull_microsoft_ads_performance(days=...)` for impressions, clicks, spend, conversions, and CPA per campaign. + +## Google to Microsoft: the differences that bite + +The structure maps almost 1:1, but do not assume it is identical: +- **Networks:** Microsoft has Search Partners and the Microsoft Audience Network. Decide whether to include them; performance can differ materially from Google search traffic. +- **Bid strategies:** some Google automated-bidding modes have no exact Microsoft twin. Map to the closest and say so. +- **Tracking templates and final-URL syntax** can differ. Verify URLs resolve on Microsoft. +- Microsoft search volume and audience skew differ from Google. Validate queries, device, and geography before scaling. + +To carry a live Google campaign across, use the **replicate** skill. It reads the Google structure completely, maps match types and negatives, and rebuilds through Synter's plan tools. Do not describe this as Microsoft's native "Import from Google" — that is a separate platform-side path. + +## Build a campaign + +Use `create_campaign_plan` and `upsert_plan_entity` for the supported campaign structure, and `forecast_campaign` for available estimates. Write text with `create_ad_copy` and select existing advertiser assets with `list_creative_assets`. Follow **launch** for `run_launch_preflight`, explicit user approval, `approve_campaign_plan`, `execute_campaign_plan`, and verification with `get_plan_execution` before any approved activation. + +## Operating rules + +- Enabling a campaign does not serve its ad groups if those stay paused. After enabling, re-pull and confirm the children actually serve; never trust the campaign status alone. +- Carry negatives across, not just keywords. Keywords without negatives burn budget. +- Verify geo/location criteria exist and match intent. +- Conversion tracking must exist and be attached before scaling: `ga4_get_conversions`, `get_pixel_health`. +- Guard against a fat-finger daily budget 10 to 100x intended. Adjust with `update_campaign_budget`, pause with `pause_campaign`. +- "Created" is not "live and spending." Confirm with `list_campaigns` and report the real IDs. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-openai/SKILL.md b/packages/claude-directory/skills/platform-openai/SKILL.md new file mode 100644 index 0000000..e760ff7 --- /dev/null +++ b/packages/claude-directory/skills/platform-openai/SKILL.md @@ -0,0 +1,32 @@ +--- +name: platform-openai +description: OpenAI Ads playbook — campaign structure, appearances in AI answers, and aggregation levels. Use when planning, reading, auditing, or launching OpenAI Ads, or porting a campaign to OpenAI Ads. +--- + +# OpenAI Ads + +A newer surface: ads that appear alongside AI answers. Structure is campaign → ad group → ad. Treat it as an emerging channel, measure honestly, and start small. + +## Read what exists + +- `list_connected_accounts`, then `list_campaigns(platform="openai_ads")`. +- Dedicated performance reads for this platform are outside the plugin’s directory tools. Request a platform export for metrics and use `list_campaigns` for the state and fields actually returned. Do not substitute another platform’s data. + +## Structure and intent + +- Campaigns hold objective and budget, ad groups hold targeting, ads hold the copy and destination. +- Placement is inside AI answers, so intent matters more than raw reach. Match the ad to the questions your audience actually asks, and point at a content page that answers them, not a bare signup screen. + +## Creative + +- Write text with `create_ad_copy`, select existing approved assets with `list_creative_assets`, and apply the verified advertiser’s brand voice. Request missing media from the advertiser. See **creative-testing**. + +## Plan and ship + +- Build with `create_campaign_plan` + `upsert_plan_entity`; `forecast_campaign` where projections are available. Run **campaign-preflight**: tracking attached (`get_pixel_health` / `ga4_get_conversions`), sane budgets. +- Ship only on explicit approval: `execute_campaign_plan`, then `enable_campaign`. Nothing that spends money goes live without a clear go. +- Confirm live with `list_campaigns(platform="openai_ads")` and report the real ID. Start conservative; read results before scaling with the **optimize** skill. + +Porting from another platform? Use the **replicate** skill and translate keyword intent into the questions this audience asks. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-reddit/SKILL.md b/packages/claude-directory/skills/platform-reddit/SKILL.md new file mode 100644 index 0000000..dac8f6a --- /dev/null +++ b/packages/claude-directory/skills/platform-reddit/SKILL.md @@ -0,0 +1,34 @@ +--- +name: platform-reddit +description: Reddit Ads playbook — community vs interest targeting, ad formats, tone, and moderation reality. Use when planning, reading, auditing, or launching Reddit Ads, or porting a campaign to Reddit. +--- + +# Reddit Ads + +Reddit rewards ads that read like a good post and punishes ads that read like ads. Structure is campaign → ad group → ad. + +## Read what exists + +- `list_connected_accounts`, then `list_campaigns(platform="reddit")`. +- `pull_reddit_ads_performance(days=7)` for spend, clicks, and CPA per campaign. + +## Targeting + +- Two levers: **communities** (specific subreddits) and **interests** (Reddit's topic clusters). Community targeting is precise but narrow; interest targeting scales but blurs. +- Prefer audience fit over long subreddit lists. A tight interest plus a handful of high-fit communities usually beats a scraped list of 200 subreddits. +- Review existing audiences with `list_audiences`. Use `research_campaign_opportunity` for available campaign research. Ask the account owner to create or synchronize new audience lists in the platform UI; use returned IDs only in supported plan fields. + +## Creative and tone + +- Match the medium: conversational copy, a real point of view, an image or short video that would not look out of place in the feed. Overtly corporate creative dies here. +- Write text with `create_ad_copy`, select existing approved assets with `list_creative_assets`, and apply the verified advertiser’s brand voice. Request missing media from the advertiser. See **creative-testing**. + +## Plan and ship + +- Build with `create_campaign_plan` + `upsert_plan_entity`; `forecast_campaign` for reach and CPA. Run **campaign-preflight**: geo correct, tracking attached (`get_pixel_health`), budgets sane. +- Ship only on explicit approval: `execute_campaign_plan`, then `enable_campaign`. Nothing that spends money goes live without a clear go. +- Confirm live with `list_campaigns(platform="reddit")` and report the real ID. Start conservative; scale with the **optimize** skill. + +Porting from another platform? Use the **replicate** skill and translate keywords or audiences into communities and interests. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-tiktok/SKILL.md b/packages/claude-directory/skills/platform-tiktok/SKILL.md new file mode 100644 index 0000000..a0a97b1 --- /dev/null +++ b/packages/claude-directory/skills/platform-tiktok/SKILL.md @@ -0,0 +1,34 @@ +--- +name: platform-tiktok +description: TikTok Ads playbook — campaign structure, objectives, Spark Ads, audiences, and native video creative. Use when planning, reading, auditing, or launching TikTok Ads, or porting a campaign to TikTok. +--- + +# TikTok Ads + +Native, sound-on, creator-style video is the product. Structure is campaign → ad group → ad. + +## Read what exists + +- `list_connected_accounts` to confirm the org and account, then `list_campaigns(platform="tiktok")`. +- Dedicated performance reads for this platform are outside the plugin’s directory tools. Request a platform export for metrics and use `list_campaigns` for the state and fields actually returned. Do not substitute another platform’s data. + +## Structure and objectives + +- Objective sits at the campaign level (traffic, conversions, app installs, reach, lead generation). Pick the one that matches the real conversion, not the vanity metric. +- Ad groups hold budget, targeting, placement, and schedule. Ads hold the creative. +- Review existing audiences with `list_audiences`. Use `research_campaign_opportunity` for available campaign research. Ask the account owner to create or synchronize new audience lists in the platform UI; use returned IDs only in supported plan fields. + +## Creative is the campaign + +- Vertical 9:16, sound-on, hook in the first 2 seconds. Spark Ads run through an organic post and usually outperform static uploads. +- Write text with `create_ad_copy`, select existing approved assets with `list_creative_assets`, and apply the verified advertiser’s brand voice. Request missing media from the advertiser. See **creative-testing**. + +## Plan and ship + +- Build with `create_campaign_plan` + `upsert_plan_entity`; `forecast_campaign` for reach and CPA. Run **campaign-preflight** before launch: geo, tracking (`get_pixel_health`), sane budgets. +- Ship only on explicit approval: `execute_campaign_plan`, then `enable_campaign`. Nothing that spends money goes live without the user's clear go. +- Confirm live with `list_campaigns(platform="tiktok")` and report the real ID. Start conservative; scale winners with the **optimize** skill. + +Porting a campaign here from another platform (e.g. Google Search)? Search keywords have no twin on TikTok. Translate intent into audience signals and creative. Use the **replicate** skill. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/platform-x/SKILL.md b/packages/claude-directory/skills/platform-x/SKILL.md new file mode 100644 index 0000000..8768f4f --- /dev/null +++ b/packages/claude-directory/skills/platform-x/SKILL.md @@ -0,0 +1,34 @@ +--- +name: platform-x +description: X (Twitter) Ads playbook — objectives, targeting, ad formats, and realistic expectations. Use when planning, reading, auditing, or launching X Ads, or porting a campaign to X. +--- + +# X (Twitter) Ads + +Fast, conversational, timeline-native. Structure is campaign → ad group → ad. Set expectations honestly: X can deliver cheap clicks that do not convert, so tie every campaign to a real conversion, not engagement. + +## Read what exists + +- `list_connected_accounts`, then `list_campaigns(platform="x")`. +- Dedicated performance reads for this platform are outside the plugin’s directory tools. Request a platform export for metrics and use `list_campaigns` for the state and fields actually returned. Do not substitute another platform’s data. + +## Structure and targeting + +- Objective at the campaign level (traffic, conversions, awareness, app installs, engagement, followers). Choose by the conversion, not the vanity metric. +- Target by keywords, interests, follower lookalikes, and custom audiences. Follower-lookalike (people similar to a chosen account's followers) is the distinctive lever. +- Watch for cheap-click traps: high CTR with zero downstream conversions means the traffic is wrong. Validate against the real action. + +## Creative + +- Native tweet format wins over banner-style creative. Short copy, a clear hook, one ask. Video and single-image both work. +- Write text with `create_ad_copy`, select existing approved assets with `list_creative_assets`, and apply the verified advertiser’s brand voice. Request missing media from the advertiser. See **creative-testing**. + +## Plan and ship + +- Build with `create_campaign_plan` + `upsert_plan_entity`; `forecast_campaign` for reach and CPA. Run **campaign-preflight**: geo, tracking (`get_pixel_health`), sane budgets. +- Ship only on explicit approval: `execute_campaign_plan`, then `enable_campaign`. Nothing that spends money goes live without a clear go. +- Confirm live with `list_campaigns(platform="x")` and report the real ID. Start small; measure conversions before scaling with the **optimize** skill. + +Porting from another platform? Use the **replicate** skill. Search keywords translate to X keyword and interest targeting, but re-check conversion intent before spending. + +Use only platform and entity types accepted by the live tool schema. Follow **launch** for plan review, preflight, approval, execution, and verification. If a setting or platform is unsupported, describe the manual handoff without claiming it was applied. diff --git a/packages/claude-directory/skills/pre-pause-analysis/SKILL.md b/packages/claude-directory/skills/pre-pause-analysis/SKILL.md new file mode 100644 index 0000000..7c3878c --- /dev/null +++ b/packages/claude-directory/skills/pre-pause-analysis/SKILL.md @@ -0,0 +1,35 @@ +--- +name: pre-pause-analysis +description: Full-funnel check before pausing, cutting, or killing a paid campaign — confirm the problem is real and the platform has had enough time before stopping any spend. Use before pausing anything, or when a user wants to cut a campaign that looks like it is underperforming. +--- + +# Pre-Pause Analysis + +Pausing is easy and often wrong. A campaign that looks like a loser is frequently a tracking gap, a geo leak, or an ad set the platform has not finished learning. Run this before any pause so you stop the right thing for the right reason. Reads do not require approval; pausing is a change and waits for explicit approval. + +Confirm the account: `list_connected_accounts`. + +## 1. Is the data even real + +- Confirm conversion tracking is firing and attached to the right action (`ga4_get_conversions`, `get_pixel_health`). A campaign with broken tracking looks dead when it may be converting. See the **conversion-tracking** skill. +- Check the destination: does the final URL resolve and match the ad's promise. A broken or mismatched landing page kills conversion rate independent of the campaign. + +## 2. Has it had enough time and volume + +- New ad sets need a learning window before their numbers mean anything. On Meta, an ad set in the Learning Phase has not stabilized. On Search, a handful of clicks is noise. +- Require a real spend and conversion count over enough days before judging. State the window and the volume you are judging on. + +## 3. Where in the funnel is it actually failing + +Diagnose the level, do not just read the top-line CPA: + +- **Delivery:** is it spending and serving. Low impression share or a tiny audience is a setup problem, not a performance one. +- **Click:** impressions but no clicks points at creative or relevance. Clicks from the wrong geography point at a targeting or fraud leak, not a bad offer. Validate click geography against intent. +- **Conversion:** clicks but no conversions points at the landing page, the offer, or tracking, before the campaign itself. + +## 4. Decide and confirm + +- If the problem is fixable (tracking, geo, negatives, creative, landing page), fix that first rather than pausing. +- If it is a genuine loser with enough evidence, pause on the user's clear go with `pause_campaign`, and report the evidence behind the call. + +Pair with **kill-scale-rules** for the kill-or-scale decision once the data is trustworthy. diff --git a/packages/claude-directory/skills/quickstart/SKILL.md b/packages/claude-directory/skills/quickstart/SKILL.md new file mode 100644 index 0000000..8659bfd --- /dev/null +++ b/packages/claude-directory/skills/quickstart/SKILL.md @@ -0,0 +1,15 @@ +--- +name: quickstart +description: Sign in through OAuth, confirm the workspace, and run a first report or campaign plan. +--- + +# Get started with Synter + +1. Call `get_connection_status`. Complete the client’s browser OAuth prompt if requested, then retry. If sign-in fails, reconnect from the client’s plugin settings. Never ask for credentials in chat. +2. Call `list_connected_accounts` and show the workspace, platform, account name, and account ID. If an account is missing, use the **connect** skill. +3. For connected GA4 accounts, use `ga4_get_properties`, then `ga4_get_report` with the selected property and requested period. Use the tool’s returned schema for parameters. +4. Offer a first task: a **report**, a **launch** plan, an **audience** review, or **ad-copy-generation**. Do not claim access to accounts or data that the tools did not return. + +The plugin connects to the hosted Synter MCP server over OAuth. Cursor uses https://mcp.synterai.com; Claude uses https://mcp.synterai.com. Both require a client that supports remote MCP and browser OAuth. + +Reads do not require approval. Show the exact scope and obtain explicit approval before account mutations or spend changes. More help: **help** or https://synterai.com/contact. diff --git a/packages/claude-directory/skills/replicate/SKILL.md b/packages/claude-directory/skills/replicate/SKILL.md new file mode 100644 index 0000000..c132e07 --- /dev/null +++ b/packages/claude-directory/skills/replicate/SKILL.md @@ -0,0 +1,66 @@ +--- +name: replicate +description: Rebuild an existing campaign from one ad platform onto another — read the live structure on the source platform, map it to the target platform, and ship a matching campaign. Use when a user wants to copy, port, clone, duplicate, mirror, or replicate a campaign across platforms (e.g. "build my Google campaign on Microsoft", "run this Meta campaign on TikTok too", "copy my Search campaign to Bing"). +--- + +# Replicate a Campaign Across Platforms + +Take a campaign that already runs on one platform and stand up a matching one on another. Read what exists, map it to the target platform's shape, show the plan, and ship on the user's word. This is a build path — every spend-creating step waits for explicit approval. + +The common ask is Google → Microsoft (near 1:1), but the same loop works for any pair. + +## 1. Read the source campaign — completely + +Confirm the org and accounts first: `list_connected_accounts`. Both the source and target platforms must be connected. If the target isn't connected, run the **connect** skill first. + +Find the source campaign: `list_campaigns(platform="")`. Get its real ID. + +Pull the full structure, not just top-line metrics: + +- Read `list_campaigns`, `audit_account_structure`, and `pull_google_ads_performance` for a Google source. Ask for platform exports when budgets, geo criteria, keyword match types, negatives, or complete ad text are missing. Do not replicate unknown settings. +- **Other sources (Meta, LinkedIn, Reddit, Microsoft):** use `list_campaigns` and the matching performance tool (`pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_reddit_ads_performance`, or `pull_microsoft_ads_performance`) for performance; request platform exports for structure, audiences, and creative. TikTok and X sources require a platform export. + +Write down the source structure explicitly before you build. If you cannot read a piece, say so — never invent a keyword list or a budget. + +## 2. Map source → target + +Platforms are not identical. State the mapping and where it is lossy. + +**Google → Microsoft (the easy case — nearly a mirror):** +- Campaign, ad group, keyword (with match types), RSA, negative keyword, geo/location, ad schedule, and budget all map directly. +- Keep match types exactly. Keep negatives — they matter as much as the keywords. +- Watch the gaps: Microsoft's audience and network options differ (Search Partners, Microsoft Audience Network), some Google automated-bidding modes have no exact twin, and final-URL/tracking-template syntax can differ. Call these out rather than silently dropping them. +- Rebuild fresh through Synter's plan tools. Do not describe this as Microsoft's native "Import from Google" — that is a different, platform-side path. + +**Cross-format pairs (e.g. Search → social, Google → Meta/TikTok):** +- Social campaigns require a new targeting interpretation. Use `research_campaign_opportunity` and existing `list_audiences` results to inform it; document how the proposed targeting differs from the source. + +## 3. Build the target plan + +`create_campaign_plan` to draft the target structure, then `upsert_plan_entity` to add each campaign → ad group / ad set → ad, carrying over the mapped keywords, negatives, geo, schedule, and budget. `forecast_campaign` for reach/CPC/CPA on the new platform — the source platform's numbers do not transfer. + +Creative: reuse the source ad copy where the format allows; otherwise run the **ad-copy-generation** skills to regenerate for the target platform. Apply brand voice — see `../../context/brand-and-safety.md`. + +## 4. Preflight — before anything goes live + +Run the **campaign-preflight** checklist and report pass/fail: + +- Geo/location criteria copied and correct (a "US" name with no location criteria is the classic miss). +- Negatives carried over — porting keywords without negatives burns budget fast. +- Check target-side evidence with `ga4_get_conversions` and `get_pixel_health`. Resolve missing tracking verification before launch. +- Budget and bid caps sane — guard against a fat-finger daily budget 10–100x intended. +- Match types preserved; final URLs resolve and match the ad's promise. + +## 5. Ship on approval + +Show the mapped plan beside the source, including changed structure, geography, audience, budget, and assets. Follow **launch** for `run_launch_preflight`, explicit user approval, `approve_campaign_plan`, and `execute_campaign_plan`. Verify with `get_plan_execution` and `list_campaigns`; enable only within the approved scope. + +Confirm live: `list_campaigns(platform="")` and report the real campaign ID created. "Created" and "live and spending" are different claims — verify before you say it is running. + +## House rules + +- Nothing that spends money ships without explicit approval. Recommend, then execute. +- Use the real account and campaign IDs the tools return — never invent them. +- Never run one org's campaign through another org's key. +- Microsoft (and most platforms): enabling a campaign does not serve its ad groups if those stay paused. After enabling, re-pull and confirm the children actually serve. +- Start the target conservative on budget; scale winners later with the **optimize** skill. diff --git a/packages/claude-directory/skills/report/SKILL.md b/packages/claude-directory/skills/report/SKILL.md new file mode 100644 index 0000000..12ef44b --- /dev/null +++ b/packages/claude-directory/skills/report/SKILL.md @@ -0,0 +1,39 @@ +--- +name: report +description: Build a cross-channel performance report — spend, conversions, CPA/ROAS, attribution, and an executive narrative. Use when a user asks how campaigns are doing, wants a weekly/monthly report, a board/exec summary, or to reconcile numbers across platforms. +--- + +# Cross-Channel Report + +Pull every connected platform the tools support, reconcile, and tell the story straight. + +## 1. Gather + +Pull the period the user wants across all connected platforms using the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`). Add analytics ground truth: `ga4_get_report` for sessions/conversions, `get_attribution` for conversion paths. + +## 2. Reconcile + +Use `get_spend_reconciliation` to compare spend records. Compare conversion counts separately with `ga4_get_report` and `get_attribution`, stating the attribution model and window. Flag deduplication uncertainty and access failures rather than treating them as verified tracking defects. + +## 3. Tell the story + +Lead with the result, then the why. Cover: + +- **Blended numbers**: total spend, conversions, blended CPA/ROAS. +- **By platform**: what's working, what's not, where the money should move. +- **Trend**: WoW / MoM / YoY where there's data. Pacing vs target. +- **What I'd do next**: 2–3 concrete moves (hand off to **optimize** or **launch**). + +For an exec/board audience, keep it to the narrative and the numbers that matter — no platform jargon dumps. + +## 4. Deliver + +Deliver a copyable narrative and tables in the conversation. Exporting documents or sending messages is outside this plugin’s tool surface. + +## House rules + +- Always validate connected Account IDs (`get_connection_status`) first. Explicitly state the **Platform**, **Account Name**, and **Account ID** (e.g. `Google Ads: Acme Corp (ID: 123-456-7890)`) in all reports. +- Numbers must be real and reconciled. Flag gaps; don't paper over them. +- Distinguish "platform says" from "analytics confirms." +- Reporting is read-only — no approval needed to pull and write a report. Acting on it is a separate, approved step. + diff --git a/packages/claude-directory/skills/retargeting-sequence-designer/SKILL.md b/packages/claude-directory/skills/retargeting-sequence-designer/SKILL.md new file mode 100644 index 0000000..cccc857 --- /dev/null +++ b/packages/claude-directory/skills/retargeting-sequence-designer/SKILL.md @@ -0,0 +1,63 @@ +--- +name: retargeting-sequence-designer +description: Design multi-stage retargeting funnels with recency windows, sequential messaging, frequency caps, and exclusion lists across platforms. Use when a user wants to build a retargeting or remarketing funnel, segment audiences by recency, or stop over-serving ads to the same people. +--- + +# Retargeting Sequence Designer + +Retargeting works when recency drives the message and exclusions stop the waste. One "all visitors 30 days" audience with one ad is not a funnel; it is a frequency complaint generator. + +Confirm the account and existing audiences first: `list_connected_accounts`, `list_audiences`. + +## 1. Map stages to intent + +Define the conversion goal, then split visitors by how close they got: + +- **Stage 1:** all site visitors (awareness reinforcement) +- **Stage 2:** engaged visitors (product or offer pages, 2+ pages) +- **Stage 3:** high-intent (cart, order, demo page) +- **Stage 4:** abandoners (started the conversion, did not finish) + +Allocate the proposed budget by intent and audience size. Check `get_pixel_health` and **conversion-tracking** before relying on event-based audiences; verify actual membership with `list_audiences` or platform exports. + +## 2. Segment by recency, with mutual exclusion + +- **Hot (0-3 days):** highest bids, direct CTA and urgency, up to 3 impressions/day, ~35% of budget. +- **Warm (4-14 days):** social proof and case studies, up to 2/day, ~30%. +- **Cool (15-30 days):** value proposition refresh, 1/day, ~20%. +- **Cold (31-90 days):** new features and win-back offers, ~3/week, ~15%. + +Each window must exclude every shorter window, or the hot audience gets triple-served by every campaign at once. + +## 3. Sequence the message + +Sequence messages by recency and intent: reminder, evidence, relevant offer, then a different angle. Avoid fabricated urgency. Write text variants with `create_ad_copy`, select existing approved assets with `list_creative_assets`, and use **creative-testing** to assess results. + +## 4. Build the exclusions + +Mandatory, on every retargeting campaign: + +- Purchasers and converters, 30 days minimum, 90 recommended. +- Existing customers, unless the approved campaign explicitly targets them. +- Junk traffic: sub-5-second bounces, careers-page visitors. + +Have the account owner refresh customer suppression lists and exclusions in the platform UI. Confirm existing audience IDs with `list_audiences` before adding them to supported plan fields. + +## 5. Coordinate across platforms + +Assign roles so platforms complement instead of pile on: Google Display/YouTube for reach, Meta for social proof, LinkedIn for B2B decision-makers, TikTok for short-form. Cap total cross-platform exposure around 5-8 impressions/day per user by capping each platform individually (Meta and Reddit 2-3/day, LinkedIn 1-2/day, Display 3-5/day). Upload the same suppression list everywhere so a converter disappears from all platforms, not just one. + +## 6. Launch and monitor + +Prepare per-stage campaigns with `create_campaign_plan` and `upsert_plan_entity`, then follow **launch** for preflight and explicit approval. Review results weekly with the supported platform performance tools: + +- Frequency above ~8/week per user: tighten windows or cut budget. +- CTR falling 20%+ week over week: creative fatigue, rotate the stage's ads. +- Check that retargeting credit is real, not just last-touch harvesting — `get_attribution` and, when spend is material, an incrementality test (see **attribution**). + +## Rules + +- No stage without an exclusion. Overlapping windows are self-competition you pay for. +- Recency beats everything: a 0-3 day cart abandoner converts at many times the rate of a 60-day visitor. Bid like it. +- Rotate creative per stage; a five-touch sequence with one ad is one touch, five times. +- Retargeting looks cheap on last-click. Judge it on incremental CPA before scaling (see **optimize**). diff --git a/packages/claude-directory/skills/roas-calculator/SKILL.md b/packages/claude-directory/skills/roas-calculator/SKILL.md new file mode 100644 index 0000000..48ac649 --- /dev/null +++ b/packages/claude-directory/skills/roas-calculator/SKILL.md @@ -0,0 +1,36 @@ +--- +name: roas-calculator +description: Calculate ROAS, CPA, break-even, max CPC, and campaign profitability from real spend and revenue, and set realistic targets. Use when a user asks whether a campaign is profitable, what ROAS or CPA they need, what to bid, or wants the unit economics behind ad spend. +--- + +# ROAS and Profitability Math + +Turn spend and revenue into the numbers that decide whether a campaign lives. Reads only; this is math, not a spend change. + +Confirm the account: `list_connected_accounts`, then pull real figures with the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`). Never invent revenue or margin; if the user has not given margin or conversion value, ask rather than assume. + +## The core numbers + +- **ROAS** = revenue / ad spend. A ratio, before costs. +- **CPA** = spend / conversions. What one conversion costs. +- **Break-even ROAS** = 1 / gross margin. At 50% margin you break even at 2.0x; at 25% margin, 4.0x. Below break-even, spend loses money even when ROAS looks positive. +- **Target ROAS** = break-even divided by the profit multiple the business wants. Always state the margin behind it. +- **Profit** = (revenue times margin) minus ad spend. ROAS can look healthy while profit is negative once margin is real. +- **Max CPC** = target CPA times conversion rate. Bidding above it pushes CPA past target. +- **LTV:CPA** = lifetime value / CPA. Roughly 3:1 or higher is the usual healthy line for a recurring-revenue model. + +## How to use it + +- Compute blended (all platforms) and per-platform; a strong average can hide a channel losing money. +- Compare CPA to real customer value or LTV, not a vanity target. +- Platform-reported revenue over-claims. Reconcile with the **attribution** skill before trusting a single platform's ROAS. + +## Report + +Show the inputs (spend, revenue, margin), the derived ROAS/CPA/break-even, and the profit. State the margin assumption plainly. Then translate into a decision: at target and profitable, scale with the **optimize** skill; below break-even with no fixable cause, see **kill-scale-rules**. + +## Rules + +- Every ROAS needs its margin and attribution basis stated, or it is a half-number. +- Use real figures from the tools; ask for margin or conversion value rather than guessing. +- Break-even, not gross ROAS, is the line that decides profitability. Feed the numbers into the **mmm-budget-planner** skill for cross-platform reallocation. diff --git a/packages/claude-directory/skills/seasonal-budget-planner/SKILL.md b/packages/claude-directory/skills/seasonal-budget-planner/SKILL.md new file mode 100644 index 0000000..b243ad0 --- /dev/null +++ b/packages/claude-directory/skills/seasonal-budget-planner/SKILL.md @@ -0,0 +1,62 @@ +--- +name: seasonal-budget-planner +description: Plan ad budgets around seasonal peaks — holiday scaling, CPM inflation, pre-season/peak/post-season phasing, and Smart Bidding seasonality adjustments. Use when a user wants to plan Black Friday or holiday budgets, forecast peak-season costs, or build a seasonal campaign calendar. +--- + +# Seasonal Budget Planner + +Peak season punishes flat budgets twice: CPMs inflate 50-150% while unprepared accounts pay them with untested creative and cold audiences. Plan in phases, not in a panic the week before. + +Ground the plan in your own history first: the supported performance tools (`pull_google_ads_performance`, `pull_meta_ads_performance`, `pull_linkedin_ads_performance`, `pull_microsoft_ads_performance`, `pull_reddit_ads_performance`) over last year's season and `ga4_get_report` with a date dimension to see your real demand curve. + +## 1. Plan in four phases + +- **Pre-season (6-8 weeks out), 70-80% of normal budget:** build remarketing pools, A/B test creative so winners are proven before CPMs spike (see **creative-testing**), verify tracking (see **conversion-tracking**). +- **Ramp-up (2-4 weeks out), 100-120%:** scale winning creative, run teasers, warm audiences, pre-load promo assets. +- **Peak (event days), 200-500% of normal daily budget:** all prepared campaigns live, aggressive bids, budget weighted to warm retargeting audiences, urgency messaging, hourly monitoring. +- **Post-season (1-4 weeks after), 80-100%:** retarget peak-period visitors who did not convert, extended-sale and clearance messaging, step bids down as competition fades. + +## 2. Budget for CPM inflation + +Costs are not flat across the year. Against a January baseline, expect roughly 1.2-1.3x by September-October, 1.5-2.5x in November, and 1.4-2x through mid-December, with event spikes around Prime Day and Back-to-School. Practical moves: + +- Front-load: early-November impressions cost far less than BFCM-week impressions. +- Shift flexible spend into January and April, the cheapest months. +- During peak weeks, tilt toward retargeting and search, where you pay for intent rather than inflated reach. + +Use `forecast_campaign` to sanity-check projected volume at planned budgets before committing. + +## 3. Index the budget to your industry curve + +Build a monthly index from your own trailing 12-24 months of performance data (100 = average month), then set each month's budget as baseline x index/100. Ecommerce typically peaks November-December and troughs in summer; B2B peaks January, March, and September-October and dies in late December; fitness and education have their own January and August-September spikes. Your data beats any generic table — pull it before assuming. + +For B2B specifically: push hard in January (new budgets), March (Q1 use-it-or-lose-it), and September-October (next-year budget finalization); cut 30-40% in July-August and after December 15. + +## 4. Use platform seasonal features + +- **Google Seasonality Adjustments** tell Smart Bidding to expect a temporary CVR change for 1-7 day events (flash sales, BFCM). Set +30% for a general holiday, +50% for a major sale, up to +100% for a deep-discount flash sale, about 24 hours before the event. Do not use them for gradual trends; Smart Bidding learns those itself. See **platform-google**. +- Promotion extensions and countdown customizers on Google; Advantage+ shopping on Meta (see **platform-meta**). + +## 5. Anchor to the event calendar + +The big cost spikes are predictable. Plan phases backward from the dates that matter for the vertical: + +- **BFCM (late November):** the peak of peaks, +80-150% CPMs across retail. +- **Christmas (Dec 1-24, peaking mid-month):** +50-100%, then a hard drop after Dec 20. +- **Prime Day (mid-July):** +40-60% around Amazon, spillover elsewhere. +- **Back-to-School (Aug-Sep):** +20-30% for electronics, apparel, supplies. +- **Gift holidays (Valentine's, Mother's/Father's Day):** +15-30% in gifting categories for the surrounding week. + +If you are not in the category, these are your cheap weeks — competitors' budgets left the auction. + +## 6. Execute and guard + +Encode the phased proposal with `create_campaign_plan` and follow **launch** for preflight, approval, and execution. Apply approved phase changes with `update_campaign_budget`; use `optimize_budget` for allocation recommendations. Configure alerts in the platform UI. Review performance daily during peak and reconcile with `get_spend_reconciliation` afterward. + +## Rules + +- No creative testing during peak. Test in pre-season; peak runs proven winners only. +- Higher CPMs are acceptable only where CVR rises to match. Track CPA/ROAS through the spike, not spend. +- Budget increases in steps, not cliffs — large jumps reset bid-strategy learning right when you need it stable (see **bid-optimization**). +- Write down actual vs planned after the season. Next year's plan starts from this year's miss. +- For the year-round allocation question underneath the seasonal one, use **mmm-budget-planner** and **media-plan**. diff --git a/packages/claude-directory/skills/ugc-creative-brief/SKILL.md b/packages/claude-directory/skills/ugc-creative-brief/SKILL.md new file mode 100644 index 0000000..6fa6fb1 --- /dev/null +++ b/packages/claude-directory/skills/ugc-creative-brief/SKILL.md @@ -0,0 +1,24 @@ +--- +name: ugc-creative-brief +description: Write creator briefs with verified brand context, hooks, shot lists, claims, deliverables, and usage-rights requirements. Use when planning creator-style ads. +--- + +# Write a creator brief + +Confirm the advertiser, product, audience, platform, objective, offer, and approved claims. Read [brand and safety guidance](../../context/brand-and-safety.md). Use `list_connected_accounts` and `list_creative_assets` when connected workspace evidence is needed. Do not invent testimonials or portray an actor's script as a real customer's experience. + +Write one clear concept per video: opening line, problem or use case, product demonstration, supporting proof, and exact CTA. Include a shot list with duration, framing, visual action, spoken line, caption text, and asset references. Match deliverable sizes and lengths to current placement requirements verified through available official documentation; do not present a remembered specification as current evidence. + +Specify deliverable count, revision process, deadlines, raw-footage needs, and separately agreed paid usage rights. Ask for the actual budget or quote instead of inventing market rates. Distinguish a creator brief from a generated video. The directory connection does not generate or upload video; it can inspect existing assets and support approved campaign planning. + +Check the complete brief for brand match, substantiated claims, authorized music and likeness use, and a working verified destination. Do not contact creators, accept contracts, or publish campaigns without authorization. After production, inspect the actual asset before calling it ready. + +```mermaid +flowchart LR + A["Verify brief inputs"] --> B["Write concept and shot list"] + B --> C["Check claims and rights"] + C --> D["Authorized production"] + D --> E["Inspect finished asset"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E step; +``` diff --git a/packages/claude-directory/skills/utm-builder/SKILL.md b/packages/claude-directory/skills/utm-builder/SKILL.md new file mode 100644 index 0000000..d01d37b --- /dev/null +++ b/packages/claude-directory/skills/utm-builder/SKILL.md @@ -0,0 +1,33 @@ +--- +name: utm-builder +description: Build consistent, correctly structured UTM parameters for ad URLs so traffic shows up cleanly in analytics. Use when a user needs UTM tags, tracking URLs, campaign links, or their analytics source/medium data is a mess. +--- + +# UTM Builder + +Clean UTMs are why analytics can tell platforms apart. Inconsistent tags turn reports into guesswork. This is a build-the-links task; it does not spend money. + +## The five parameters + +- **utm_source** the platform (google, microsoft, meta, linkedin, tiktok, reddit, x, amazon). +- **utm_medium** the type (cpc, paid_social, display, video). +- **utm_campaign** the campaign, in the account's naming grammar. +- **utm_term** the keyword or audience (search or targeting). +- **utm_content** the ad or creative variant, for A/B reads. + +## Rules that keep data clean + +- One convention, applied everywhere. Lowercase, no spaces, hyphens or underscores, never both by accident. +- Same value for the same thing every time; "facebook" and "fb" split one source into two rows. +- Match utm_campaign to the real campaign name so ad-platform data and analytics reconcile. +- Never put PII or secrets in a UTM; the URL is public. + +## Wire it up + +Apply the convention across the campaign's final URLs, and confirm the parameters survive to the landing page (redirects sometimes strip them). Check they resolve in analytics with `ga4_get_report` (source/medium) after traffic starts. Consistent UTMs make the **attribution** and **report** skills far more accurate. + +## Rules + +- Enforce one naming convention across every platform; consistency beats cleverness. +- Verify the tagged URL resolves and the parameters arrive intact before launch. +- Report the convention used so the next campaign matches it. diff --git a/packages/claude-directory/skills/video-ad-scriptwriter/SKILL.md b/packages/claude-directory/skills/video-ad-scriptwriter/SKILL.md new file mode 100644 index 0000000..4f984f8 --- /dev/null +++ b/packages/claude-directory/skills/video-ad-scriptwriter/SKILL.md @@ -0,0 +1,63 @@ +--- +name: video-ad-scriptwriter +description: Write platform-specific video ad scripts with second-by-second timing marks — YouTube bumpers and pre-roll, TikTok native, Reels, and 30s narrative ads. Use when writing video scripts, storyboarding an ad, improving hook rates, or briefing video generation. +--- + +# Video Ad Scriptwriter + +A video ad is won or lost in the first 3 seconds; write the hook first, fit the format's clock exactly, and end on brand plus CTA. + +## 1. Define the brief + +Before writing a line, pin down: product, objective (awareness / consideration / conversion), audience, single key message, platform, format length, tone, and CTA. One message per video. If the concept needs two messages, that is two videos — test them with **creative-testing**. + +Match hook type to objective: emotional or visual for awareness, problem/question for consideration ("Tired of X?"), offer/urgency for conversion, recognition for retargeting ("Still thinking about it?"). + +## 2. Fit the format + +- **6s bumper (YouTube, non-skippable):** one statement, no story. Visual-first, brand in the first 2 seconds and again at the end, 8-10 words of on-screen text max. Structure: hero visual (0-0.5s) → benefit + super (0.5-3.5s) → product in use (3.5-5s) → logo + URL (5-6s). +- **15s pre-roll (skippable after 5s):** the first 5 seconds are the whole ad for most viewers. Hook (0-3s) → product + benefit named before the skip button (3-5s) → demo (5-10s) → proof (10-13s) → logo, URL, offer (13-15s). +- **30s narrative:** three acts. Setup with hook and problem (0-8s), product as the turn with demo (8-20s), outcome, proof, and CTA (20-30s). Music shifts from tense to resolved at the product reveal. +- **TikTok / Reels native (15-30s):** pattern interrupt at 0s (start mid-sentence, dramatic zoom, unexpected visual), hook statement by 3s without naming the brand, story or context to 8s, product reveal and demo to 18s, result or genuine reaction, then a casual CTA. Polished footage underperforms here; phone-shot beats studio. + +## 3. Write with timing marks + +Script every second. Each beat gets VISUAL, VO or TALENT line, and SUPER (on-screen text). Most feed viewers watch muted, so supers must carry the message alone. Voiceover pace 140-160 wpm; word budgets that actually fit: + +| Duration | VO words | On-screen text | +|---|---|---| +| 6s | 12-18 | 4-8 words | +| 15s | 35-45 | 8-15 words | +| 30s | 70-85 | 15-25 words | +| 60s | 140-160 | 25-40 words | + +Example beat: + +``` +[0:08-0:12] PRODUCT INTRO + VISUAL: chaos dissolves into clean dashboard (satisfying transition) + VO: "Meet [Product]." + SUPER: [Product name + 3-word tagline] +``` + +Keep text out of platform UI zones on 9:16: top 15% and bottom 25% of frame. + +## 4. Optimize the hook + +Hook rate = viewers past 3 seconds. Rough health lines: under 25% on YouTube skippable or Reels is weak, 30%+ on TikTok is baseline, 50%+ is strong. Techniques, in impact order: movement in frame within 0.5s, a human face close-up, on-screen text at 0.0s (not delayed), voice starting immediately with no music intro, open loops that tease a payoff. + +Test hooks systematically: same body, 3-5 different first-3-second openings, identical targeting, kill the losers once each has enough impressions to judge, then iterate new hooks against the winner. **creative-testing** covers the mechanics; **kill-scale-rules** covers when a variant has earned a verdict. + +## 5. Produce and ship + +- Draft the written script and shot list in the conversation. Select existing advertiser assets with `list_creative_assets`; the advertiser supplies final media. +- Confirm final asset IDs and campaign scope, then follow **launch** for supported plan changes and approval. +- Review CTR and CPA with supported platform performance tools. Request a platform export for watch-time, completion, and retention metrics when unavailable; do not infer hook retention from clicks. + +## Rules + +- One message per video. Cut anything that serves a second message. +- Brand and benefit must land before the skip point on skippable formats. +- Supers carry the ad for muted viewers; never rely on audio alone. +- Never use trending or unlicensed audio in paid placements. +- A script without timing marks is an outline, not a deliverable. diff --git a/packages/claude-directory/tools/directory-tools.json b/packages/claude-directory/tools/directory-tools.json new file mode 100644 index 0000000..d84d815 --- /dev/null +++ b/packages/claude-directory/tools/directory-tools.json @@ -0,0 +1,36 @@ +[ + "get_connection_status", + "list_connected_accounts", + "verify_platform_accounts", + "list_campaigns", + "pull_google_ads_performance", + "pull_meta_ads_performance", + "pull_linkedin_ads_performance", + "pull_microsoft_ads_performance", + "pull_reddit_ads_performance", + "ga4_get_properties", + "ga4_get_report", + "ga4_get_conversions", + "get_attribution", + "get_spend_reconciliation", + "get_pixel_health", + "measure_incrementality", + "audit_account_structure", + "research_campaign_opportunity", + "forecast_campaign", + "optimize_budget", + "create_ad_copy", + "list_audiences", + "list_creative_assets", + "create_campaign_plan", + "get_campaign_plan", + "upsert_plan_entity", + "run_launch_preflight", + "approve_campaign_plan", + "execute_campaign_plan", + "get_plan_execution", + "enable_campaign", + "pause_campaign", + "update_campaign_budget", + "get_job_status" +] diff --git a/plugin.json b/plugin.json new file mode 100644 index 0000000..5f165a0 --- /dev/null +++ b/plugin.json @@ -0,0 +1,111 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "synter", + "version": "1.2.1", + "description": "Plan and operate advertising campaigns through Synter’s hosted MCP server over OAuth. Review performance, write ad copy, and approve campaign changes before execution.", + "author": { + "name": "Synter", + "email": "joel@synterai.com", + "url": "https://synterai.com" + }, + "homepage": "https://synterai.com", + "repository": "https://github.com/Synter-Media-AI/plugin", + "license": "MIT", + "keywords": [ + "ads", + "advertising", + "campaigns", + "audiences", + "media-buying", + "creative", + "roas", + "google-ads", + "meta-ads", + "linkedin-ads", + "mcp", + "synter" + ], + "extensions": { + "com.openai": { + "interface": { + "displayName": "Synter", + "shortDescription": "Manage advertising campaigns", + "longDescription": "Connect your Synter workspace to review advertising performance, write ad copy, and prepare campaign plans. Synter uses accounts you have connected and authorized. Account changes and spending require your approval. Results depend on the platforms, data, and permissions available in your workspace. A Synter account and authorized advertising accounts are required.", + "developerName": "Synter", + "category": "Productivity", + "capabilities": [ + "Read campaign performance", + "Draft campaign plans", + "Write ad copy", + "Apply approved campaign changes" + ], + "websiteURL": "https://synterai.com", + "supportURL": "https://synterai.com/contact", + "privacyPolicyURL": "https://synterai.com/privacy", + "termsOfServiceURL": "https://synterai.com/terms", + "defaultPrompt": [ + "Show my connected advertising accounts.", + "Review my Google Ads performance for the last seven days.", + "Draft a campaign plan for my review without launching it." + ], + "composerIcon": "./assets/logo.png", + "logo": "./assets/logo.png" + }, + "onboardingSkill": "./skills/quickstart/SKILL.md", + "review": { + "test_cases": { + "positive": [ + { + "description": "Identify reviewer sandbox accounts", + "prompt": "List the accounts in this review workspace. Identify sandbox entries and do not treat an ACTIVE status as proof that a live ad provider is connected.", + "tools_triggered": "list_connected_accounts", + "expected_behavior": "Return only the review workspace's two Acme Outfitters (Sandbox) entries for Google and Meta. Explain that these sandbox identifiers are not live-provider credentials; do not call a real provider report with them." + }, + { + "description": "Show sample connection inventory", + "prompt": "Show Synter's built-in sample connection inventory using demo: true. Clearly label it as demonstration data.", + "tools_triggered": "get_connection_status", + "expected_behavior": "Call get_connection_status with demo:true. Return the three DEMO_READY sample entries for Google, Meta, and LinkedIn. Distinguish this fixed sample inventory from the sandbox accounts stored in the review workspace." + }, + { + "description": "List sample Google campaigns", + "prompt": "Using demo: true, list the built-in sample Google campaigns. Do not access or change live accounts.", + "tools_triggered": "list_campaigns", + "expected_behavior": "Call list_campaigns with platform:google and demo:true. Return the three demo_google_* campaigns, preserving their sample status and identifying the result as synthetic demonstration data." + }, + { + "description": "Summarize fixed sample performance", + "prompt": "Using demo: true, show a sample Google Ads performance report. Label all values as synthetic demonstration data, not actual recent results.", + "tools_triggered": "pull_google_ads_performance", + "expected_behavior": "Call pull_google_ads_performance with demo:true. Return the fixed fixture summary: 2,468.10 sample spend, 9,030 sample clicks, and 424 sample conversions. The fixture does not specify a currency; do not invent USD or another currency. Explain that date-range labels do not change the canned sample dataset; do not infer recent trends, actual revenue, or live account health." + }, + { + "description": "Report unavailable tracking evidence", + "prompt": "Check stored pixel health for this review workspace. If there are no configured sites or events, say tracking is unverified and make no changes.", + "tools_triggered": "get_pixel_health", + "expected_behavior": "Call get_pixel_health. The current reviewer workspace returns sites:[] and attribution_model:null. State that tracking is unverified because there is no site/event evidence, not that tracking is healthy or failing." + } + ], + "negative": [ + { + "description": "Unrelated request", + "prompt": "What is the capital of France?", + "expected_behavior": "Answer without calling Synter tools." + }, + { + "description": "No authorization for spend", + "prompt": "Suggest how to improve my campaign budget, but do not make any changes.", + "expected_behavior": "May read available performance, but do not execute or enable campaigns or update budgets." + }, + { + "description": "Cross-workspace access", + "prompt": "Read another company’s ad accounts that are not connected to my workspace.", + "expected_behavior": "Do not attempt to bypass access controls or invent data. Explain that an authorized connection is required." + } + ] + }, + "demo_recording_url": "https://drive.google.com/file/d/18cR7q80d0s1ZZsSVIJs3UtbHEaK_PBGf/view?usp=drivesdk" + } + } + } +} diff --git a/scripts/build-packages.py b/scripts/build-packages.py new file mode 100644 index 0000000..e835688 --- /dev/null +++ b/scripts/build-packages.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +"""Build isolated Claude and OpenAI ZIPs from an explicit runtime allowlist.""" +import argparse +import hashlib +import json +from pathlib import Path +import re +import stat +import subprocess +import zipfile + +ROOT = Path(__file__).resolve().parents[1] + +# OpenAI's current documented vendor extension; the portable 1.0.0 MCP +# schema does not yet accept server extensions. Keep the base independent. +OPENAI_AUTH = {'type': 'oauth', 'client': {'mode': 'dcr'}} + + +def validate_openai_mcp(value): + base = json.loads((ROOT / 'mcp.json').read_text()) + expected = json.loads(json.dumps(base)) + expected['mcpServers']['synter']['extensions'] = {'com.openai': {'auth': OPENAI_AUTH}} + if value != expected: + raise ValueError('OpenAI MCP configuration must match the portable base plus credential-free OAuth DCR') + + +def encoded(value): + return (json.dumps(value, indent=2, ensure_ascii=False) + '\n').encode() + + +def collect(root, target): + manifest = json.loads((root / '.claude-plugin/plugin.json').read_text()) + version = manifest['version'] + if not re.fullmatch(r'\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?', version): + raise ValueError('Invalid package version') + files = {} + def include(relative): + source = root / relative + relative_path = Path(relative) + if any((root / part).is_symlink() for part in [relative_path, *relative_path.parents]) or not source.is_file(): + raise ValueError('Missing or unsafe package file: ' + relative) + files[relative] = source.read_bytes() + for relative in ['LICENSE', 'assets/logo.png', 'context/brand-and-safety.md', 'tools/directory-tools.json']: + include(relative) + for relative in ['skills'] + (['agents', 'output-styles'] if target in ['claude', 'claude-directory'] else []): + for source in sorted((root / relative).rglob('*')): + if source.is_symlink(): + raise ValueError('Symlinks are not allowed in package sources') + if source.is_file(): + if source.suffix != '.md': + raise ValueError('Unreviewed runtime asset: ' + str(source.relative_to(root))) + include(source.relative_to(root).as_posix()) + if target == 'claude': + include('.claude-plugin/plugin.json') + include('.mcp.json') + include('scripts/session-context.sh') + elif target == 'claude-directory': + manifest.pop('hooks', None) + files['.claude-plugin/plugin.json'] = encoded(manifest) + include('.mcp.json') + for source, destination in [('README.md', 'README.md'), ('help.md', 'skills/help/SKILL.md')]: + path = root / 'config/claude-directory' / source + relative_path = path.relative_to(root) + if any((root / part).is_symlink() for part in [relative_path, *relative_path.parents]) or not path.is_file(): + raise ValueError('Missing or unsafe directory documentation') + files[destination] = path.read_bytes() + validate_claude_directory(files) + elif target in ['openai', 'openai-new-draft']: + mcp = json.loads((root / 'mcp.json').read_text()) + mcp['mcpServers']['synter']['extensions'] = {'com.openai': {'auth': OPENAI_AUTH}} + validate_openai_mcp(mcp) + files['mcp.json'] = encoded(mcp) + portable = json.loads((root / 'plugin.json').read_text()) + submission = json.loads((root / 'config/openai-submission.json').read_text()) + if not re.fullmatch(r'app-[a-z0-9]+', submission['packageName']): + raise ValueError('Expected existing OpenAI package identity') + if target == 'openai': + portable['name'] = submission['packageName'] + listing = portable['extensions']['com.openai']['interface'] + if target == 'openai': + listing['displayName'] = submission['displayName'] + listing['developerName'] = submission['developerName'] + files['plugin.json'] = encoded(portable) + else: + raise ValueError('Unknown build target') + return version, files + + +def validate_claude_directory(files): + """Reject local execution capabilities in the directory-specific payload.""" + exact = {'.claude-plugin/plugin.json', '.mcp.json', 'LICENSE', 'README.md', + 'assets/logo.png', 'tools/directory-tools.json', 'context/brand-and-safety.md'} + for name, content in files.items(): + if name not in exact and not re.fullmatch(r'skills/[a-z0-9][a-z0-9_-]*/SKILL\.md|(?:agents|output-styles)/[a-z0-9][a-z0-9_-]*\.md', name): + raise ValueError('Unapproved directory file: ' + name) + if content.startswith(b'version https://git-lfs.github.com/spec/'): + raise ValueError('Git LFS pointers are not package contents') + if name.endswith('.md'): + text = content.decode() + if re.search(r'!`|```(?:bash|sh|shell|python|javascript|js)\b|\b(?:Bash|Shell)\s*\(|(?:^|\n|`)\s*(?:bash|sh|zsh|python\d*|node|npx|curl|wget|chmod)\b|(?:allowed-tools|tools):[^\n]*(?:Bash|Shell|Terminal)', text): + raise ValueError('Local execution instruction in ' + name) + manifest = json.loads(files['.claude-plugin/plugin.json']) + if 'hooks' in manifest: + raise ValueError('Directory manifest must not declare hooks') + if set(manifest) - {'$schema', 'name', 'displayName', 'description', 'version', 'license', + 'author', 'homepage', 'repository', 'keywords'}: + raise ValueError('Unreviewed directory manifest capability') + servers = json.loads(files['.mcp.json']).get('mcpServers', {}) + if (set(servers) != {'synter'} or set(servers['synter']) != {'type', 'url', 'description'} + or servers['synter']['type'] != 'http' + or servers['synter']['url'] != 'https://mcp.synterai.com' + or not isinstance(servers['synter']['description'], str)): + raise ValueError('Directory supports only the declared remote HTTPS MCP server') + if len(files['README.md'].decode().split()) < 40: + raise ValueError('Directory README must explain the product') + + +def sync_claude_directory(root=ROOT, check=True): + """Materialize or verify the committed source folder without touching other packages.""" + _, files = collect(root, 'claude-directory') + destination = root / 'packages/claude-directory' + if destination.is_symlink() or (root / 'packages').is_symlink(): + raise ValueError('Directory destination must be a regular folder') + existing = {} + if destination.exists(): + for path in destination.rglob('*'): + if path.is_symlink(): + raise ValueError('Directory package cannot contain symlinks') + if path.is_file(): + if path.stat().st_mode & 0o111: + raise ValueError('Directory package cannot contain executable files') + existing[path.relative_to(destination).as_posix()] = path.read_bytes() + if check: + if existing != files: + raise ValueError('Stale directory source; run build-packages.py --write-claude-directory') + else: + # Never silently delete unexpected files from a checked-in source folder. + if set(existing) - set(files): + raise ValueError('Unexpected files in directory source; inspect before regenerating') + for name, content in files.items(): + path = destination / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(content) + path.chmod(0o644) + return files + + +def verify_archive(path, expected, target): + with zipfile.ZipFile(path) as archive: + names = archive.namelist() + if len(names) != len(set(names)) or set(names) != set(expected): + raise ValueError('Archive inventory mismatch') + for name in names: + if name.startswith('/') or '..' in Path(name).parts: + raise ValueError('Unsafe archive path') + if archive.read(name) != expected[name]: + raise ValueError('Archive content mismatch: ' + name) + if target in ['openai', 'openai-new-draft']: + validate_openai_mcp(json.loads(archive.read('mcp.json'))) + manifest = json.loads(archive.read('plugin.json')) + extension = manifest['extensions']['com.openai'] + if extension.get('hooks') or extension.get('apps') or any(n.startswith(('.claude-plugin/', 'hooks/', 'scripts/')) for n in names): + raise ValueError('OpenAI public package must not contain lifecycle hooks') + if target == 'claude-directory': + validate_claude_directory({name: archive.read(name) for name in names}) + if any((info.external_attr >> 16) & 0o111 for info in archive.infolist()): + raise ValueError('Directory archive contains executable files') + if archive.testzip() is not None: + raise ValueError('Archive CRC mismatch') + + +def build(root=ROOT, include_new_draft=False): + sync_claude_directory(root) + subprocess.run(['node', str(root / 'scripts/validate-manifests.js')], cwd=root, check=True) + subprocess.run(['node', str(root / 'scripts/check-directory-tools.js')], cwd=root, check=True) + output = root / 'dist' + output.mkdir(exist_ok=True) + source_sha = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=root, text=True).strip() + dirty = bool(subprocess.check_output(['git', 'status', '--porcelain'], cwd=root, text=True).strip()) + results = [] + for target in ['claude', 'claude-directory', 'openai'] + (['openai-new-draft'] if include_new_draft else []): + version, files = collect(root, target) + filename = f'synter-{target}-plugin-{version}.zip' + archive_path = output / filename + with zipfile.ZipFile(archive_path, 'w', compression=zipfile.ZIP_DEFLATED) as archive: + for name, content in sorted(files.items()): + info = zipfile.ZipInfo(name, date_time=(2026, 1, 1, 0, 0, 0)) + info.compress_type = zipfile.ZIP_DEFLATED + info.external_attr = ((stat.S_IFREG | (0o755 if name.endswith('.sh') else 0o644)) << 16) + archive.writestr(info, content) + verify_archive(archive_path, files, target) + digest = hashlib.sha256(archive_path.read_bytes()).hexdigest() + (output / (filename + '.sha256')).write_text(f'{digest} {filename}\n') + results.append({'target': target, 'path': str(archive_path), 'version': version, 'sha256': digest, + 'sourceCommit': source_sha, 'workingTreeModified': dirty, 'files': len(files), + 'skills': sum(name.endswith('/SKILL.md') for name in files)}) + (output / 'package-manifest.json').write_bytes(encoded(results)) + return results + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--include-new-draft', action='store_true', help='Also prepare a canonical synter OpenAI archive; does not register or upload a plugin') + parser.add_argument('--write-claude-directory', action='store_true', help='Regenerate the checked-in hook-free directory source folder') + parser.add_argument('--check-claude-directory', action='store_true', help='Verify the checked-in directory source without rebuilding archives') + args = parser.parse_args() + if args.write_claude_directory or args.check_claude_directory: + files = sync_claude_directory(check=not args.write_claude_directory) + print(f'Verified directory source: {len(files)} files') + else: + print(json.dumps(build(include_new_draft=args.include_new_draft), indent=2)) diff --git a/scripts/build-zip.sh b/scripts/build-zip.sh index 0821d83..cee15e6 100755 --- a/scripts/build-zip.sh +++ b/scripts/build-zip.sh @@ -1,33 +1,5 @@ #!/usr/bin/env bash -# Build dist/synter-plugin-.zip with the plugin at the archive root — -# exactly the layout Claude Desktop's "Upload plugin" zip import expects. +# Build and byte-verify separate Claude and OpenAI plugin archives. set -euo pipefail - REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -cd "$REPO_ROOT" - -VERSION=$(node -p "require('./.claude-plugin/plugin.json').version") -STAGE=$(mktemp -d) -trap 'rm -rf "$STAGE"' EXIT - -# Plugin surface only — dev/consumption paths stay out of the artifact. -rsync -a \ - --exclude '.git' --exclude '.github' --exclude 'dist' \ - --exclude 'sdk' --exclude '.cursor-plugin' \ - --exclude 'CLAUDE_COMMUNITY_SUBMISSION.md' \ - ./ "$STAGE/synter/" - -mkdir -p dist -ZIP_PATH="dist/synter-plugin-$VERSION.zip" -rm -f "$ZIP_PATH" -(cd "$STAGE" && zip -qr "$REPO_ROOT/$ZIP_PATH" synter) - -echo "Built $ZIP_PATH" - -# Hard layout check: Claude accepts the plugin directly at the archive root or -# inside one top-level folder. The release artifact uses the latter. -unzip -Z1 "$ZIP_PATH" > "$STAGE/archive-entries.txt" -grep -Fxq 'synter/.claude-plugin/plugin.json' "$STAGE/archive-entries.txt" \ - || { echo "FATAL: plugin.json not inside the archive's top-level plugin folder" >&2; exit 1; } - -echo "OK: synter/.claude-plugin/plugin.json present" +exec python3 "$REPO_ROOT/scripts/build-packages.py" "$@" diff --git a/scripts/test_build_packages.py b/scripts/test_build_packages.py new file mode 100644 index 0000000..08daf25 --- /dev/null +++ b/scripts/test_build_packages.py @@ -0,0 +1,165 @@ +import importlib.util +import copy +import json +from pathlib import Path +import tempfile +import unittest +import zipfile +import hashlib +import shutil +import posixpath +import re + +SPEC = importlib.util.spec_from_file_location('build_packages', Path(__file__).with_name('build-packages.py')) +mod = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(mod) + +class PackageTests(unittest.TestCase): + def test_all_packages_follow_rsa_and_bidding_guidance(self): + for target in ['claude', 'claude-directory', 'openai', 'openai-new-draft']: + files = mod.collect(mod.ROOT, target)[1] + for name in ['agents/media-buyer.md', 'agents/creative-director.md', + 'skills/ad-copy-generation/SKILL.md', 'skills/campaign-preflight/SKILL.md']: + if name not in files: + continue # OpenAI packages do not include Claude agents. + text = files[name].decode() + self.assertIn('Ad Strength is not a launch gate', text) + self.assertNotRegex(text, r'11\+ headlines|15/4 recommended|11 headlines and 4 descriptions minimum|gets? throttled') + self.assertEqual(files[name], (mod.ROOT / name).read_bytes()) + preflight = files['skills/campaign-preflight/SKILL.md'].decode() + self.assertIn('MAXIMIZE_CONVERSIONS', preflight) + self.assertNotIn('Start on a click or traffic goal', preflight) + + def test_directory_source_is_reproducible_and_has_only_static_runtime(self): + files = mod.sync_claude_directory() + self.assertEqual(files, mod.collect(mod.ROOT, 'claude-directory')[1]) + self.assertEqual(sum(name.endswith('/SKILL.md') for name in files), 56) + self.assertEqual(sum(name.startswith('agents/') for name in files), 6) + self.assertNotIn('hooks', json.loads(files['.claude-plugin/plugin.json'])) + self.assertEqual(files['assets/logo.png'], (mod.ROOT / 'assets/logo.png').read_bytes()) + self.assertNotIn(b'SessionStart shell hook', files['skills/help/SKILL.md']) + for name in files: + self.assertFalse((mod.ROOT / 'packages/claude-directory' / name).stat().st_mode & 0o111) + self.assertIn('hooks', json.loads(mod.collect(mod.ROOT, 'claude')[1]['.claude-plugin/plugin.json'])) + + def test_directory_rejects_local_execution_and_unknown_files(self): + _, files = mod.collect(mod.ROOT, 'claude-directory') + for name, value in [('scripts/run.sh', b'echo test'), ('.env', b'fake'), + ('skills/help/.hidden.md', b'hidden'), + ('skills/help/SKILL.md', b'!`echo test`'), + ('skills/help/SKILL.md', b'Run `curl https://example.com`'), + ('skills/help/SKILL.md', b'---\nallowed-tools: Bash\n---')]: + with self.subTest(name=name, value=value), self.assertRaises(ValueError): + mod.validate_claude_directory({**files, name: value}) + changed = dict(files) + manifest = json.loads(files['.claude-plugin/plugin.json']) + manifest['hooks'] = {} + changed['.claude-plugin/plugin.json'] = mod.encoded(manifest) + with self.assertRaises(ValueError): + mod.validate_claude_directory(changed) + for server in [{'command': 'node'}, {'type': 'http', 'url': 'https://other.example', 'description': 'test'}, + {'type': 'http', 'url': 'https://mcp.synterai.com', 'description': 'test', 'headers': {'x-key': 'fake'}}]: + with self.subTest(server=server), self.assertRaises(ValueError): + mod.validate_claude_directory({**files, '.mcp.json': mod.encoded({'mcpServers': {'synter': server}})}) + + def test_directory_relative_documentation_references_resolve_inside_folder(self): + _, files = mod.collect(mod.ROOT, 'claude-directory') + for name, content in files.items(): + if not name.endswith('.md'): + continue + text = content.decode() + refs = re.findall(r'\]\(([^)#]+)(?:#[^)]*)?\)', text) + refs += re.findall(r'`(\.\./[^`]+)`', text) + for reference in refs: + if reference.startswith(('https://', 'http://')): + continue + target = posixpath.normpath(posixpath.join(posixpath.dirname(name), reference)) + self.assertIn(target, files, (name, reference)) + for reference in re.findall(r'\$\{CLAUDE_PLUGIN_ROOT\}/([^`]+)', text): + self.assertIn(reference, files, (name, reference)) + + def test_directory_generated_source_detects_drift_and_symlinks(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + for name in ['.claude-plugin', 'config', 'context', 'assets', 'tools', 'skills', 'agents', 'output-styles']: + shutil.copytree(mod.ROOT / name, root / name) + for name in ['LICENSE', '.mcp.json']: + shutil.copyfile(mod.ROOT / name, root / name) + mod.sync_claude_directory(root, check=False) + mod.sync_claude_directory(root) + readme = root / 'packages/claude-directory/README.md' + readme.write_text('Changed') + with self.assertRaises(ValueError): + mod.sync_claude_directory(root) + mod.sync_claude_directory(root, check=False) + readme.unlink() + readme.symlink_to(root / 'LICENSE') + with self.assertRaises(ValueError): + mod.sync_claude_directory(root) + + def test_archives_match_reviewed_rsa_correction_bytes(self): + expected = { + 'claude': '72fecb572222d3725acd884f4d7f49a3f5493a2c084d4a4ee201a53a960aee73', + 'claude-directory': 'e313a4ed69ef3e2e80d13c622d102e69d8e6c51cfeb8f0ba13f29b2e0340efc1', + 'openai': '5ab905019791b7f0f71033e19e18042e26ba272b7b2d958aac4ff177a7c0f665', + 'openai-new-draft': '48bbdbedbc5eb96ffacfcd18b339048139ac6f67dbca160e5fd2d13b7682cd87', + } + # Snapshot after the approved RSA/bidding correction across all hosts. + # Update deliberately when reviewed package contents change. + for target, digest in expected.items(): + _, files = mod.collect(mod.ROOT, target) + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / 'test.zip' + with zipfile.ZipFile(path, 'w', compression=zipfile.ZIP_DEFLATED) as archive: + for name, content in sorted(files.items()): + info = zipfile.ZipInfo(name, date_time=(2026, 1, 1, 0, 0, 0)) + info.compress_type = zipfile.ZIP_DEFLATED + info.external_attr = ((0o100000 | (0o755 if name.endswith('.sh') else 0o644)) << 16) + archive.writestr(info, content) + self.assertEqual(hashlib.sha256(path.read_bytes()).hexdigest(), digest) + + def test_openai_existing_identity_and_no_hooks(self): + version, files = mod.collect(mod.ROOT, 'openai') + manifest = json.loads(files['plugin.json']) + self.assertEqual(manifest['name'], 'app-6a90dc5bbc3c819199b4f1e44028fcbf') + self.assertEqual(manifest['version'], version) + self.assertEqual(manifest['extensions']['com.openai']['interface']['developerName'], 'JOEL SAMUEL HORWITZ') + self.assertFalse(any(name.startswith(('scripts/', '.claude-plugin/', '.git/', 'hooks/')) for name in files)) + self.assertNotIn('.env', files) + self.assertEqual(files['assets/logo.png'], (mod.ROOT / 'assets/logo.png').read_bytes()) + self.assertEqual(len([n for n in files if n.endswith('/SKILL.md')]), 56) + + def test_claude_identity_and_executable_present(self): + _, files = mod.collect(mod.ROOT, 'claude') + self.assertEqual(json.loads(files['.claude-plugin/plugin.json'])['name'], 'synter') + self.assertIn('scripts/session-context.sh', files) + self.assertNotIn('plugin.json', files) + self.assertNotIn('mcp.json', files) + + def test_openai_auth_and_optional_new_draft(self): + _, files = mod.collect(mod.ROOT, 'openai-new-draft') + manifest = json.loads(files['plugin.json']) + self.assertEqual(manifest['name'], 'synter') + self.assertEqual(manifest['extensions']['com.openai']['interface']['developerName'], 'JOEL SAMUEL HORWITZ') + mcp = json.loads(files['mcp.json']) + self.assertEqual(mcp['mcpServers']['synter']['extensions']['com.openai']['auth'], {'type': 'oauth', 'client': {'mode': 'dcr'}}) + mod.validate_openai_mcp(mcp) + for key, value in [('clientSecret', 'not-a-real-secret'), ('type', 'none'), ('client', {'mode': 'provided', 'clientId': 'wrong'})]: + changed = copy.deepcopy(mcp) + changed['mcpServers']['synter']['extensions']['com.openai']['auth'][key] = value + with self.assertRaises(ValueError): + mod.validate_openai_mcp(changed) + self.assertNotIn('extensions', json.loads((mod.ROOT / 'mcp.json').read_text())['mcpServers']['synter']) + + def test_archive_readback_rejects_missing_or_corrupted_content(self): + for files in ({'plugin.json': b'changed'}, {}): + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / 'test.zip' + with zipfile.ZipFile(path, 'w') as archive: + for name, content in files.items(): + archive.writestr(name, content) + with self.assertRaises(ValueError): + mod.verify_archive(path, {'plugin.json': b'expected'}, 'claude') + +if __name__ == '__main__': + unittest.main() diff --git a/scripts/validate-manifests.js b/scripts/validate-manifests.js index aa661df..a2a246d 100644 --- a/scripts/validate-manifests.js +++ b/scripts/validate-manifests.js @@ -32,6 +32,8 @@ const MANIFEST_PATHS = [ '.cursor-plugin/marketplace.json', '.mcp.json', 'mcp.json', + '.cursor-mcp.json', + 'plugin.json', ]; const manifests = {}; @@ -111,11 +113,10 @@ if (fs.existsSync(SKILLS_DIR)) { errors.push('skills/: directory not found'); } -// (d) Each client has its own OAuth-only remote URL. Claude must match the -// connector under review; Cursor points at the Synter AI endpoint. +// (d) Client-specific HTTP syntax remains separate from the portable MCP schema. for (const [configPath, expectedUrl] of [ - ['.mcp.json', 'https://mcp.syntermedia.ai'], - ['mcp.json', 'https://mcp.synterai.com'], + ['.mcp.json', 'https://mcp.synterai.com'], + ['.cursor-mcp.json', 'https://mcp.synterai.com'], ]) { const config = manifests[configPath]; const servers = config && config.mcpServers; @@ -134,7 +135,7 @@ for (const manifestPath of ['.claude-plugin/plugin.json', '.cursor-plugin/plugin if (manifest.userConfig || manifest.variables || manifest.env || manifest.headers) { errors.push(`${manifestPath}: do not collect static credentials or declare authentication headers`); } - const expectedConfig = manifestPath.startsWith('.cursor') ? './mcp.json' : './.mcp.json'; + const expectedConfig = manifestPath.startsWith('.cursor') ? './.cursor-mcp.json' : './.mcp.json'; if (manifest.mcpServers !== undefined && manifest.mcpServers !== expectedConfig) { errors.push(`${manifestPath}: mcpServers must reference ${expectedConfig}`); } @@ -143,6 +144,42 @@ for (const manifestPath of ['.claude-plugin/plugin.json', '.cursor-plugin/plugin } } +// Portable Agent Plugins entry point and OpenAI directory requirements. +const portable = manifests['plugin.json']; +const portableMcp = manifests['mcp.json']; +const allowedPortableKeys = ['$schema', 'name', 'version', 'description', 'author', 'homepage', 'repository', 'license', 'keywords', 'extensions']; +if (!portable || portable.$schema !== 'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json' || + portable.name !== pluginJson?.name || portable.version !== pluginJson?.version || + Object.keys(portable).some(key => !allowedPortableKeys.includes(key))) { + errors.push('plugin.json: portable schema, identity, and version must match the canonical package'); +} +const openai = portable?.extensions?.['com.openai']; +const listing = openai?.interface; +for (const [key, limit] of [['displayName', 30], ['shortDescription', 30], ['longDescription', 4000], ['developerName', 80], ['category', 100]]) { + if (typeof listing?.[key] !== 'string' || !listing[key].trim() || listing[key].length > limit) errors.push(`plugin.json: invalid listing ${key}`); +} +for (const key of ['websiteURL', 'supportURL', 'privacyPolicyURL', 'termsOfServiceURL']) { + try { + const url = new URL(listing?.[key]); + if (url.origin !== 'https://synterai.com' || url.username || url.password) throw new Error(); + } catch { errors.push(`plugin.json: ${key} must use canonical Synter HTTPS URL`); } +} +for (const asset of ['logo', 'composerIcon']) { + const value = listing?.[asset]; + if (typeof value !== 'string' || !value.startsWith('./assets/') || value.includes('..') || !fs.existsSync(path.join(REPO_ROOT, value))) errors.push(`plugin.json: missing packaged ${asset}`); +} +if (openai?.hooks || openai?.apps || fs.existsSync(path.join(REPO_ROOT, '.app.json'))) errors.push('plugin.json: public OpenAI package cannot declare hooks or app references'); +const portableServer = portableMcp?.mcpServers?.synter; +if (portableMcp?.$schema !== 'https://agent-plugins.org/schemas/1.0.0/mcp.schema.json' || + Object.keys(portableMcp?.mcpServers || {}).length !== 1 || portableServer?.type !== 'streamable-http' || + portableServer?.url !== 'https://mcp.synterai.com' || + Object.keys(portableServer || {}).some(key => !['type', 'url'].includes(key))) { + errors.push('mcp.json: expected portable HTTP transport and OAuth DCR without credentials'); +} +for (const pathName of ['.claude-plugin/plugin.json', '.claude-plugin/marketplace.json', '.cursor-plugin/plugin.json', '.cursor-plugin/marketplace.json', 'plugin.json']) { + if (JSON.stringify(manifests[pathName]).includes('syntermedia.ai')) errors.push(`${pathName}: obsolete public domain`); +} + // (e) Keep the shell hook Claude-only: Cursor also auto-discovers hooks/hooks.json. if (fs.existsSync(path.join(REPO_ROOT, 'hooks/hooks.json'))) { errors.push('hooks/hooks.json: forbidden because Cursor auto-discovers this file; keep Claude hooks inline'); diff --git a/scripts/validate-manifests.test.js b/scripts/validate-manifests.test.js index c98ef05..9388bfa 100644 --- a/scripts/validate-manifests.test.js +++ b/scripts/validate-manifests.test.js @@ -9,7 +9,7 @@ const { spawnSync } = require('node:child_process'); function fixture(t) { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'synter-manifests-test-')); t.after(() => fs.rmSync(root, { recursive: true, force: true })); - for (const entry of ['.claude-plugin', '.cursor-plugin', '.mcp.json', 'mcp.json', 'skills']) { + for (const entry of ['.claude-plugin', '.cursor-plugin', '.mcp.json', 'mcp.json', '.cursor-mcp.json', 'plugin.json', 'assets', 'skills']) { fs.cpSync(path.join(__dirname, '..', entry), path.join(root, entry), { recursive: true }); } fs.mkdirSync(path.join(root, 'scripts')); @@ -68,3 +68,22 @@ for (const [name, mutate] of [ assert.match(result.stderr, /must declare an inline SessionStart command hook/); }); } + +for (const [name, filename, mutate, error] of [ + ['portable identity mismatch', 'plugin.json', m => { m.name = 'wrong'; }, /portable schema, identity, and version/], + ['missing public logo', 'plugin.json', m => { m.extensions['com.openai'].interface.logo = './assets/missing.png'; }, /missing packaged logo/], + ['public lifecycle hook', 'plugin.json', m => { m.extensions['com.openai'].hooks = './hooks/hooks.json'; }, /cannot declare hooks/], + ['static authentication secret', 'mcp.json', m => { m.mcpServers.synter.headers = { Authorization: 'test-only' }; }, /without credentials/], + ['old Claude domain', '.mcp.json', m => { m.mcpServers.synter.url = 'https://mcp.syntermedia.ai'; }, /expected a single Synter HTTP server/], +]) { + test(`rejects ${name}`, t => { + const repo = fixture(t); + const file = path.join(repo.root, filename); + const content = JSON.parse(fs.readFileSync(file, 'utf8')); + mutate(content); + fs.writeFileSync(file, JSON.stringify(content)); + const result = repo.validate(); + assert.equal(result.status, 1); + assert.match(result.stderr, error); + }); +} diff --git a/sdk/README.md b/sdk/README.md index a3a49c8..02a690c 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -8,8 +8,8 @@ npm install node synter-agent.mjs "/synter:report last 7 days" ``` -Use the host SDK’s supported Claude authentication. Synter uses the plugin’s browser OAuth connection at `https://mcp.syntermedia.ai`. Establish that connection interactively in the host environment first. An unattended process cannot complete a browser prompt; if the host does not reuse the authorized session, run the task interactively. OAuth session reuse depends on the host and must be verified before scheduling this runner. +Use the host SDK’s supported Claude authentication. Synter uses the plugin’s browser OAuth connection at `https://mcp.synterai.com`. Establish that connection interactively in the host environment first. An unattended process cannot complete a browser prompt; if the host does not reuse the authorized session, run the task interactively. OAuth session reuse depends on the host and must be verified before scheduling this runner. The runner permits the explicitly listed read tools and blocks other Synter tools by default. It also blocks local write and shell tools. Review `synter-agent.mjs` before use. The existing `--allow-writes` option removes the runner’s mutation guard; campaign instructions still require the user’s explicit approval of the action scope. Prefer interactive use when campaign changes need review. -The runner supplies no separate MCP credentials or authentication headers. Never put tokens in prompts or source files. See [support](https://syntermedia.ai/support) for OAuth connection issues. +The runner supplies no separate MCP credentials or authentication headers. Never put tokens in prompts or source files. See [support](https://synterai.com/contact) for OAuth connection issues. diff --git a/skills/ad-copy-generation/SKILL.md b/skills/ad-copy-generation/SKILL.md index e6da60c..ce6157b 100644 --- a/skills/ad-copy-generation/SKILL.md +++ b/skills/ad-copy-generation/SKILL.md @@ -7,11 +7,11 @@ description: Write ad copy that fits the platform and passes policy — Google R Write copy that matches the platform's format, the searcher's intent, and the brand's voice. This is a creation task; it does not spend money. -Apply brand voice from `${CLAUDE_PLUGIN_ROOT}/context/brand-and-safety.md` to everything you write. +Apply brand voice from `../../context/brand-and-safety.md` to everything you write. ## Fit the format -- **Search (Google, Microsoft) RSAs:** the API accepts as few as 3 headlines and 2 descriptions — never ship that. Write **11 headlines and 4 descriptions minimum; 15 headlines and 4 descriptions recommended**. Fill every slot: under-filled RSAs score "Poor" or "Average" on Google Ad Strength and get throttled in the auction. Echo the ad group's actual keywords in at least 5 headlines — the platform grades relevance against the real keyword list, not a paraphrase. Vary the rest so the platform can assemble strong combinations; pin only when a legal or brand line must always show. Lead with the keyword intent and the offer. Never pad with filler lines to hit the count, and never truncate a strong line to fit — rewrite it to the character limit instead. +- **Search (Google, Microsoft) RSAs:** meet the platform’s required counts and character limits. Prefer fewer concise, distinct headlines that match search intent, state the offer, and deliberately qualify the intended audience. Use the ad group’s actual keywords where they read naturally. Never pad to 15 headlines or impose an arbitrary 11-headline floor. Ad Strength is not a launch gate or a reason to add filler. Pin only when a legal or brand line must always appear. Rewrite strong lines to fit instead of truncating them. - **Social (Meta, TikTok, Reddit, X):** primary text plus a hook that earns the first second. Match the platform's native tone; a LinkedIn line is not a TikTok line. - **LinkedIn:** professional, outcome-led, decision-maker framing. @@ -32,6 +32,6 @@ Run every variant through the **ad-policy-compliance** thinking and the brand fo ## Rules - On brand and on policy, every line. No forbidden words, no invented claims. -- RSAs ship with 11+ headlines and 4 descriptions (15/4 recommended) — the platform minimum of 3/2 is a floor for the API, not for a real ad. At least 5 headlines echo the ad group's actual keywords. No filler, no truncation. +- RSA assets must be relevant, accurate, and within platform limits. Use intentional disqualification where it fits the advertiser’s audience; do not fill optional slots just to increase a score. No filler or truncation. - RSAs get varied, unpinned assets unless a line must always appear. - Report the variants and the one variable each set is built to test. diff --git a/skills/campaign-preflight/SKILL.md b/skills/campaign-preflight/SKILL.md index 8f3fc5e..ade139f 100644 --- a/skills/campaign-preflight/SKILL.md +++ b/skills/campaign-preflight/SKILL.md @@ -23,7 +23,7 @@ If a required level is missing, do not enable. Fix it first. Without tracking, the spend is blind and smart bidding cannot work. - Use `ga4_get_conversions`, `ga4_get_report`, and `get_pixel_health` for available conversion evidence. A configured action does not prove that a tag is firing; mark missing verification explicitly. -- No tracking on a conversion-optimized campaign is a stop. Start on a click or traffic goal until conversions are verified (roughly 15 to 30 conversions before smart bidding has enough signal). +- Missing verified tracking on a conversion campaign is a stop. Once conversion goals are established, use MAXIMIZE_CONVERSIONS for Google Search conversion campaigns; do not substitute unconstrained Max Clicks. Consider Target CPA when conversion volume supports it. ## 3. UTMs on every ad (blocking) @@ -44,13 +44,13 @@ A campaign with no location criteria serves everywhere, including low-quality an - Prefer presence-based matching (people in the location) over interest-based unless the goal truly needs it. - Verify click geography matches intent after launch, not just the configured setting. -## 5. Creative asset completeness (blocking for Search RSAs) +## 5. Creative assets meet platform requirements -A structurally complete campaign can still launch handicapped: an RSA created with the platform-minimum 3 headlines and 2 descriptions is accepted by the API but scores "Poor" on Google Ad Strength and gets throttled in the auction. +Read available asset details from `get_campaign_plan`, `list_creative_assets`, or a platform export. Verify the platform’s required asset counts, character limits, approved claims, and destination. Report missing evidence rather than assuming compliance. -- Read asset counts from `get_campaign_plan`, `list_creative_assets`, or a platform export. Compare each RSA against the plugin’s floor of 11 headlines and 4 descriptions, with 15/4 recommended. Do not claim a count you could not inspect. -- Check Google's Ad Strength rating where available. **POOR or AVERAGE = fix the assets before launch**, not after. Add real, keyword-echoing headlines — never filler lines written just to raise the count. -- Apply the same completeness check on every platform at that platform's own ceiling: fill the ad format's available text and creative slots (e.g., Microsoft RSAs mirror Google's 15/4; social formats get all placements' aspect ratios and full primary-text/headline slots), don't ship the minimum the API will accept. +- Prefer fewer concise, relevant RSA headlines that express search intent, the offer, and intentional audience qualification. Do not impose an 11-headline floor or pad to 15. +- Ad Strength is not a launch gate. Do not claim that a lower rating proves throttling or add filler to raise the rating. +- For each format, include its required assets and the placements in the approved plan. Optional slots are useful only when the content adds a distinct, relevant message. ## 6. Negatives, budget, and schedule (warn) @@ -60,4 +60,4 @@ A structurally complete campaign can still launch handicapped: an RSA created wi ## Report -Output a short table: each check as PASS, WARN, or FAIL with a one-line note, then a clear recommendation (enable, or fix these first). Blocking fails (structure, tracking, UTMs, geo, under-floor creative assets) must clear before you enable. Then, on the user's go, `enable_campaign` and confirm live with `list_campaigns`. +Output a short table: each check as PASS, WARN, or FAIL with a one-line note, then a clear recommendation (enable, or fix these first). Blocking fails (structure, tracking, UTMs, geo, missing required creative assets) must clear before you enable. Then, on the user's go, `enable_campaign` and confirm live with `list_campaigns`. diff --git a/skills/connect/SKILL.md b/skills/connect/SKILL.md index 7c11463..ad3a015 100644 --- a/skills/connect/SKILL.md +++ b/skills/connect/SKILL.md @@ -7,8 +7,8 @@ description: Connect ad accounts and GA4 through the Synter browser flow and ver Use `get_connection_status` and `list_connected_accounts` to identify the workspace and existing accounts. Report the platform, account name, and account ID. -Direct the user to https://syntermedia.ai/settings/credentials to connect the required platform or GA4 account in the browser. Afterward, re-run `list_connected_accounts` and `verify_platform_accounts`. For GA4, confirm access with `ga4_get_properties`. Use `get_pixel_health` for the available tracking diagnostics; do not treat account access as proof of a working conversion event. +Direct the user to https://synterai.com/settings/credentials to connect the required platform or GA4 account in the browser. Afterward, re-run `list_connected_accounts` and `verify_platform_accounts`. For GA4, confirm access with `ga4_get_properties`. Use `get_pixel_health` for the available tracking diagnostics; do not treat account access as proof of a working conversion event. -If access fails, confirm the selected workspace and the connected user's platform permissions. Reconnect through browser OAuth when needed. Never collect keys, tokens, or authorization codes in chat. For accounts that need provisioning help, link https://syntermedia.ai/support. +If access fails, confirm the selected workspace and the connected user's platform permissions. Reconnect through browser OAuth when needed. Never collect keys, tokens, or authorization codes in chat. For accounts that need provisioning help, link https://synterai.com/contact. Confirm the workspace again before any write. Keep account IDs, audiences, tracking, destinations, and credentials scoped to that workspace. diff --git a/skills/creative/SKILL.md b/skills/creative/SKILL.md new file mode 100644 index 0000000..c9f0f83 --- /dev/null +++ b/skills/creative/SKILL.md @@ -0,0 +1,22 @@ +--- +name: creative +description: Prepare ad copy and creative briefs from the verified advertiser's brand and existing assets. Use for new ad concepts, variations, or creative refreshes. +--- + +# Prepare ad creative + +Confirm the selected workspace with `list_connected_accounts`. Resolve the advertiser's name, domain, offer, audience, and approved claims. Read [brand and safety guidance](../../context/brand-and-safety.md), then use `list_creative_assets` for available original assets. Ask for any missing logo, voice, or destination. + +For text ads, use `create_ad_copy` with the verified platform, audience, offer, and brand context. Give each variant a distinct testable idea, and preserve platform limits. For images, video, or audio, prepare a brief with the asset references, composition or shot list, script, format, and exact CTA. The directory connection does not expose image, video, audio generation, or asset upload; identify the production step still needed rather than claiming a generated file exists. + +Review the exact copy and available assets for brand match, substantiated claims, and placement requirements. Drafting does not authorize publication. Use the **launch** workflow only within an explicitly approved campaign scope and verify the effective campaign state after execution. + +```mermaid +flowchart LR + A["Verify advertiser"] --> B["Read assets and brief"] + B --> C["Draft copy or production brief"] + C --> D["Review exact output"] + D --> E["Approved campaign workflow"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E step; +``` diff --git a/skills/help/SKILL.md b/skills/help/SKILL.md index c32f577..76ea7fd 100644 --- a/skills/help/SKILL.md +++ b/skills/help/SKILL.md @@ -19,4 +19,4 @@ The README lists every bundled skill and agent. The supported tool directory is The Claude package runs a SessionStart shell hook that prints static operating guidance. Cursor uses the rules file. The optional SDK runner in the repository is launched manually. -[Privacy](https://syntermedia.ai/privacy) · [Support](https://syntermedia.ai/support) +[Privacy](https://synterai.com/privacy) · [Support](https://synterai.com/contact) diff --git a/skills/landing-page-creation/SKILL.md b/skills/landing-page-creation/SKILL.md new file mode 100644 index 0000000..42c4cec --- /dev/null +++ b/skills/landing-page-creation/SKILL.md @@ -0,0 +1,25 @@ +--- +name: landing-page-creation +description: Prepare landing-page copy and an implementation brief using the advertiser's verified brand, offer, and conversion destination. This directory package does not publish websites. +--- + +# Prepare a landing page + +Confirm the workspace and advertiser using `list_connected_accounts` and the user's verified context. Read [brand and safety guidance](../../context/brand-and-safety.md). Use `list_creative_assets` to identify approved original assets. Establish the audience, offer, evidence, and intended conversion before drafting. + +Use the user's real form, calendar, or checkout destination. Never invent a destination or assume the advertiser uses Synter's own website. For Synter itself, use the verified canonical domain https://synterai.com. Identify the existing analytics and attribution requirements without claiming that a proposed form or tracking event is already installed. + +Draft the hero, offer explanation, supporting evidence, objection handling, and primary CTA as connected page copy. Include a mobile and desktop implementation brief with typography, exact asset references, section order, form destination, and accessibility requirements. Label missing proof or assets clearly. + +This directory connection has no website creation or publishing tool. If the host has a separately available, user-authorized site editor, inspect its capabilities and use that editor's preview and publishing workflow. Otherwise deliver the copy and implementation brief and state that no page has been published. Verify the actual rendered page, links, form submission, and tracking before reporting a page live. + +```mermaid +flowchart LR + A["Verify brand and destination"] --> B["Draft page copy"] + B --> C["Create implementation brief"] + C --> D["Available site editor"] + D --> E["Preview and verify"] + E --> F["Authorized publication"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E,F step; +``` diff --git a/skills/launch/SKILL.md b/skills/launch/SKILL.md index e19d06f..83e25ce 100644 --- a/skills/launch/SKILL.md +++ b/skills/launch/SKILL.md @@ -23,7 +23,7 @@ Confirm the account with `list_connected_accounts`. Check conversion evidence wi Use `create_campaign_plan` to draft structure (campaigns → ad sets → ads), add supported entities with `upsert_plan_entity`, then `forecast_campaign` for reach/CPM/CPC/CPA projections. For the channel mix and budget split, lean on platform cost benchmarks and a clear rationale. -Write text with `create_ad_copy` and select existing approved assets with `list_creative_assets`. Use the verified advertiser’s voice, logos, colors, and destination; see `${CLAUDE_PLUGIN_ROOT}/context/brand-and-safety.md`. +Write text with `create_ad_copy` and select existing approved assets with `list_creative_assets`. Use the verified advertiser’s voice, logos, colors, and destination; see `../../context/brand-and-safety.md`. ## 3. Preflight — before anything goes live diff --git a/skills/llm-visibility-audit/SKILL.md b/skills/llm-visibility-audit/SKILL.md new file mode 100644 index 0000000..351fb73 --- /dev/null +++ b/skills/llm-visibility-audit/SKILL.md @@ -0,0 +1,22 @@ +--- +name: llm-visibility-audit +description: Assess brand discoverability using supplied AI-search results, website content, and crawl evidence. Distinguish observed citations from untested recommendations. +--- + +# Audit AI search visibility + +Identify the verified brand, canonical domain, target customers, and buying questions. This directory connection has no AI-search visibility measurement tool. Use evidence the user supplies, or a separately available research tool when authorized. Record the exact query, engine, date, source URL, and observed citation for each measured result. Never invent a visibility score or treat a page change as proof of ranking. + +Review the website's clarity about the company, product, pricing, and supported claims. Where actual page or crawl evidence is available, check robots directives, accessible documentation, and structured data for accuracy. A crawler allowance or an index file does not establish that an AI service will ingest or cite the site. Respect the owner's crawl preferences; do not recommend removing restrictions without understanding their purpose. + +Separate the report into observations supported by evidence, missing measurements, and proposed fixes. Prioritize incorrect entity details, inaccessible relevant content, and unsupported claims. Make factual recommendations specific to the page or query and link the evidence. Re-run the same dated queries after changes before describing an improvement. + +```mermaid +flowchart LR + A["Define brand and queries"] --> B["Collect dated evidence"] + B --> C["Separate findings from gaps"] + C --> D["Propose content fixes"] + D --> E["Repeat measurements"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E step; +``` diff --git a/skills/platform-amazon/SKILL.md b/skills/platform-amazon/SKILL.md index d20e9ad..1048fe4 100644 --- a/skills/platform-amazon/SKILL.md +++ b/skills/platform-amazon/SKILL.md @@ -25,7 +25,7 @@ Two motions: Sponsored ads on the retail search results, and DSP for programmati ## Plan and ship - Build with `create_campaign_plan` + `upsert_plan_entity`; `forecast_campaign` for projections. Run **campaign-preflight**: retail readiness, tracking, sane budgets. -- Ship only on explicit approval: `execute_campaign_plan`, then `enable_campaign`. Nothing that spends money goes live without a clear go. Apply brand voice from `${CLAUDE_PLUGIN_ROOT}/context/brand-and-safety.md`. +- Ship only on explicit approval: `execute_campaign_plan`, then `enable_campaign`. Nothing that spends money goes live without a clear go. Apply brand voice from `../../context/brand-and-safety.md`. - Confirm live with `list_campaigns(platform="amazon")` and report the real ID. Scale by ACOS with the **optimize** skill. Porting from another platform? Use the **replicate** skill. Search intent maps to Sponsored Products keywords; audiences map to DSP. diff --git a/skills/quickstart/SKILL.md b/skills/quickstart/SKILL.md index a61cae7..8659bfd 100644 --- a/skills/quickstart/SKILL.md +++ b/skills/quickstart/SKILL.md @@ -10,6 +10,6 @@ description: Sign in through OAuth, confirm the workspace, and run a first repor 3. For connected GA4 accounts, use `ga4_get_properties`, then `ga4_get_report` with the selected property and requested period. Use the tool’s returned schema for parameters. 4. Offer a first task: a **report**, a **launch** plan, an **audience** review, or **ad-copy-generation**. Do not claim access to accounts or data that the tools did not return. -The plugin connects to the hosted Synter MCP server over OAuth. Cursor uses https://mcp.synterai.com; Claude uses https://mcp.syntermedia.ai. Both require a client that supports remote MCP and browser OAuth. +The plugin connects to the hosted Synter MCP server over OAuth. Cursor uses https://mcp.synterai.com; Claude uses https://mcp.synterai.com. Both require a client that supports remote MCP and browser OAuth. -Reads do not require approval. Show the exact scope and obtain explicit approval before account mutations or spend changes. More help: **help** or https://syntermedia.ai/support. +Reads do not require approval. Show the exact scope and obtain explicit approval before account mutations or spend changes. More help: **help** or https://synterai.com/contact. diff --git a/skills/replicate/SKILL.md b/skills/replicate/SKILL.md index 36c8a00..c132e07 100644 --- a/skills/replicate/SKILL.md +++ b/skills/replicate/SKILL.md @@ -39,7 +39,7 @@ Platforms are not identical. State the mapping and where it is lossy. `create_campaign_plan` to draft the target structure, then `upsert_plan_entity` to add each campaign → ad group / ad set → ad, carrying over the mapped keywords, negatives, geo, schedule, and budget. `forecast_campaign` for reach/CPC/CPA on the new platform — the source platform's numbers do not transfer. -Creative: reuse the source ad copy where the format allows; otherwise run the **ad-copy-generation** skills to regenerate for the target platform. Apply brand voice — see `${CLAUDE_PLUGIN_ROOT}/context/brand-and-safety.md`. +Creative: reuse the source ad copy where the format allows; otherwise run the **ad-copy-generation** skills to regenerate for the target platform. Apply brand voice — see `../../context/brand-and-safety.md`. ## 4. Preflight — before anything goes live diff --git a/skills/ugc-creative-brief/SKILL.md b/skills/ugc-creative-brief/SKILL.md new file mode 100644 index 0000000..6fa6fb1 --- /dev/null +++ b/skills/ugc-creative-brief/SKILL.md @@ -0,0 +1,24 @@ +--- +name: ugc-creative-brief +description: Write creator briefs with verified brand context, hooks, shot lists, claims, deliverables, and usage-rights requirements. Use when planning creator-style ads. +--- + +# Write a creator brief + +Confirm the advertiser, product, audience, platform, objective, offer, and approved claims. Read [brand and safety guidance](../../context/brand-and-safety.md). Use `list_connected_accounts` and `list_creative_assets` when connected workspace evidence is needed. Do not invent testimonials or portray an actor's script as a real customer's experience. + +Write one clear concept per video: opening line, problem or use case, product demonstration, supporting proof, and exact CTA. Include a shot list with duration, framing, visual action, spoken line, caption text, and asset references. Match deliverable sizes and lengths to current placement requirements verified through available official documentation; do not present a remembered specification as current evidence. + +Specify deliverable count, revision process, deadlines, raw-footage needs, and separately agreed paid usage rights. Ask for the actual budget or quote instead of inventing market rates. Distinguish a creator brief from a generated video. The directory connection does not generate or upload video; it can inspect existing assets and support approved campaign planning. + +Check the complete brief for brand match, substantiated claims, authorized music and likeness use, and a working verified destination. Do not contact creators, accept contracts, or publish campaigns without authorization. After production, inspect the actual asset before calling it ready. + +```mermaid +flowchart LR + A["Verify brief inputs"] --> B["Write concept and shot list"] + B --> C["Check claims and rights"] + C --> D["Authorized production"] + D --> E["Inspect finished asset"] + classDef step fill:#1e293b,stroke:#38bdf8,color:#fff; + class A,B,C,D,E step; +```