Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
6 changes: 3 additions & 3 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 8 additions & 0 deletions .cursor-mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"synter": {
"type": "http",
"url": "https://mcp.synterai.com"
}
}
}
2 changes: 1 addition & 1 deletion .cursor-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
{
Expand Down
6 changes: 3 additions & 3 deletions .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -29,5 +29,5 @@
"skills": "./skills/",
"agents": "./agents/",
"rules": "./rules/",
"mcpServers": "./mcp.json"
"mcpServers": "./.cursor-mcp.json"
}
30 changes: 24 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,40 @@ 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"

- 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
24 changes: 23 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/
2 changes: 1 addition & 1 deletion .mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
91 changes: 77 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,45 @@
<img src="assets/logo.png" alt="Synter" width="128" height="128" />
</p>

# 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
Expand All @@ -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`.
Expand All @@ -31,21 +58,23 @@ 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

The plugin connects to the hosted Synter MCP server over OAuth. The advertising tools run on that server. Local Markdown skills, agents, and rules guide how the client uses them; the checked-in [tool directory](tools/directory-tools.json) contains the 34 supported tool names.

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:<name>`):

- [`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)
Expand All @@ -62,7 +91,7 @@ Bundled skills (invoke as `/synter:<name>`):

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

Expand All @@ -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
```
Expand All @@ -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).
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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).
2 changes: 1 addition & 1 deletion agents/creative-director.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Loading
Loading