diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index d5a2275..8284e67 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "iterable-sdk", - "version": "26.10.10-beta", + "version": "26.10.11-beta", "description": "Takes an Android or iOS app — native or React Native — from nothing to a push notification proven on a device: Firebase or Apple (APNs) provisioning, version-pinned SDK integration, silent-failure pitfalls, and device-side proof that a push actually arrived. Android proof runs on a device or emulator; iOS proof runs on the iOS Simulator.", "author": { "name": "Iterable" }, "repository": "https://github.com/Iterable/iterable-sdk-skill", diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 02ee9d6..3f3fd1b 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -3,7 +3,7 @@ "owner": { "name": "Iterable" }, "metadata": { "description": "Iterable's official skills for AI coding assistants — mobile SDK integration guidance.", - "version": "26.10.10-beta" + "version": "26.10.11-beta" }, "plugins": [ { diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index f02ae5c..c8bb958 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "iterable-sdk", - "version": "26.10.10-beta", + "version": "26.10.11-beta", "description": "Takes an Android or iOS app — native or React Native — from nothing to a push notification proven on a device: Firebase or Apple (APNs) provisioning, version-pinned SDK integration, silent-failure pitfalls, and device-side proof that a push actually arrived. Android proof runs on a device or emulator; iOS proof runs on the iOS Simulator.", "author": { "name": "Iterable" }, "repository": "https://github.com/Iterable/iterable-sdk-skill", diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index c5280c5..0000000 --- a/.mcp.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "mcpServers": { - "context7": { - "type": "http", - "url": "https://mcp.context7.com/mcp" - } - } -} diff --git a/README.md b/README.md index 551c022..8e9a231 100644 --- a/README.md +++ b/README.md @@ -135,16 +135,62 @@ select **iterable**, and select **Enable auto-update**. ### Cursor -Cursor requires version 3.9 or later. Install the skills as a plugin: +Cursor installs plugins from a marketplace, so you first add this repository +as a marketplace and then install the plugin from it. Before you start, sign +in to Cursor and update it to the latest version. -1. Open **Customize** in the sidebar. +1. Open **Customize** in the sidebar, then open **Plugins**. 2. Select **+ Add**, then **From GitHub Repository**. -3. Enter `https://github.com/Iterable/iterable-sdk-skill`. -4. Find **iterable-sdk**, select **Install**, and choose the project or user - scope. +3. In **Import Marketplace**, enter + `https://github.com/Iterable/iterable-sdk-skill` under **Repository**. Leave + **Scope** set to **User**, then select **Import**. +4. Cursor switches to the marketplace browser. Select **Personal** or + **Iterable**, whichever the marketplace is listed under, to see its plugins. + Then open **iterable-sdk**. +5. On the plugin's page, select **Install**. If Cursor asks for a scope, choose + the project or user scope. -The plugin installs the skills, the scripts they run, and the Context7 MCP -server. Team admins can also add the repository as a team marketplace. +The plugin installs the skills and the scripts they run. To confirm the +install, open **Customize** and check that: + +- **Plugins** lists **iterable-sdk**. +- **Skills** lists the six `iterable-*` skills. + +Then start a new Agent chat. + +If **From GitHub Repository** is missing, open the marketplace from +**Customize**, select **Add Marketplace**, then **Import from GitHub**. + +Who can import depends on your plan: + +- **Free and Pro:** follow the steps above. +- **Teams:** the steps above work, because community plugin import is on by + default. +- **Enterprise:** community plugin import is off by default. Ask an admin to + allow it, or to add the repository as a team marketplace. + +If you installed the plugin in Claude Code, Cursor also loads that copy while +**Include Third-Party Plugins, Skills, and Other Configs** is on, in +**Cursor Settings → Agents → Third-Party Imports**. That copy updates through +Claude Code. + +#### Team marketplace + +On Teams and Enterprise, an admin can share the plugin with the whole team: + +1. Go to **Dashboard → Plugins & MCPs**. +2. In **Team Marketplaces**, click **Add Marketplace**, choose + **Import from Repo**, and paste the repository URL. +3. Add **iterable-sdk** with **Add to Marketplace**. +4. Under **Marketplace Settings**, set **Marketplace Access**, then save. +5. Choose how the plugin is distributed: **Default Off**, **Default On**, or + **Required**. + +Teammates then find the plugin in **Customize**. The Teams plan allows one +imported team marketplace. Auto Refresh needs the Cursor GitHub App on the +repository, so select **Refresh** to pick up a new version. + +#### Local checkout To test a local checkout, clone it into Cursor's local plugin folder: @@ -153,13 +199,15 @@ git clone https://github.com/Iterable/iterable-sdk-skill.git ~/.cursor/plugins/l ``` Run **Developer: Reload Window**, then confirm the skills in **Customize**. -Cursor skips a symlink in that folder that points to a clone elsewhere. On -Teams and Enterprise plans, an admin must allow local plugin imports. +Cursor skips a symlink in that folder that points to a clone elsewhere. If +**iterable-sdk** is also installed from a marketplace, that install takes +precedence over the local copy. On Enterprise, an admin must turn on +**Allow Local Plugin Imports**. #### Fallback: link the skill directories If you can't install plugins, clone the repository and link the skill -directories. This installs the skills without the Context7 MCP server: +directories: ```bash git clone --depth 1 https://github.com/Iterable/iterable-sdk-skill.git ~/iterable-skills @@ -239,8 +287,7 @@ notifications afterwards is optional. - Push proof creates or updates one proof template in your Iterable project and sends it only to the test user. The test identity becomes a real user profile in that project. -- The plugin also adds the Context7 MCP server (no API key) for documentation - lookups and, in Claude Code, five helper subagents. +- In Claude Code, the plugin also adds five helper subagents. - You authenticate in your own browser or `gcloud` session. The scripts do not collect account passwords or attempt to bypass sign-in, CAPTCHA, or terms-of-service screens. @@ -295,9 +342,12 @@ codex plugin marketplace upgrade iterable Then start a new Codex session. -For Cursor, manage the installed plugin from **Customize**. For a local -plugin checkout, run `git pull` in `~/.cursor/plugins/local/iterable-sdk` and -reload the window. For the clone-and-link fallback: +For Cursor, the plugin is indexed when you import the repository. To get a +newer version, refresh the marketplace from **Customize**, or remove it and +import it again. For a team marketplace, an admin +selects **Refresh** in **Dashboard → Plugins & MCPs**. For a local plugin +checkout, run `git pull` in `~/.cursor/plugins/local/iterable-sdk` and reload +the window. For the clone-and-link fallback: ```bash cd ~/iterable-skills @@ -319,8 +369,10 @@ codex plugin remove iterable-sdk@iterable codex plugin marketplace remove iterable ``` -For Cursor, uninstall the plugin from **Customize**, or delete -`~/.cursor/plugins/local/iterable-sdk` for a local checkout. For the +For Cursor, open **iterable-sdk** in **Customize** and select **Uninstall**. +You can then remove the marketplace. That removes it from your Cursor account +and leaves the repository unchanged. For a local +checkout, delete `~/.cursor/plugins/local/iterable-sdk`. For the clone-and-link fallback, delete the links in `~/.cursor/skills/` and the `~/iterable-skills` clone. diff --git a/mcp.json b/mcp.json deleted file mode 100644 index c5280c5..0000000 --- a/mcp.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "mcpServers": { - "context7": { - "type": "http", - "url": "https://mcp.context7.com/mcp" - } - } -} diff --git a/pipeline/src/validate-plugins.ts b/pipeline/src/validate-plugins.ts index 9607545..b2995d3 100644 --- a/pipeline/src/validate-plugins.ts +++ b/pipeline/src/validate-plugins.ts @@ -1,11 +1,11 @@ /** - * Validates Cursor/Claude plugin manifests and MCP config files. + * Validates Cursor/Claude plugin manifests. * * Checks: * - `.cursor-plugin/plugin.json` and `.cursor-plugin/marketplace.json` parse * and have required fields. * - `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json` parse. - * - `mcp.json` and `.mcp.json` exist and have identical `mcpServers` content. + * - No manifest declares MCP servers; the skills read the docs they ship. * - Declared skill paths exist on disk. * - Every manifest declares the same, valid version. * @@ -93,7 +93,7 @@ function validateCursorPlugin(): void { if ("mcpServers" in manifest) { fail( file, - "omit `mcpServers` — Cursor auto-discovers `mcp.json` at the plugin root", + "omit `mcpServers` — the plugin bundles no MCP server", ); } } @@ -151,39 +151,6 @@ function validateClaudeMarketplace(): void { } } -function validateMcpConfigs(): void { - const cursorMcp = readJson("mcp.json"); - const claudeMcp = readJson(".mcp.json"); - if (!isRecord(cursorMcp) || !isRecord(claudeMcp)) return; - - const cursorServers = cursorMcp.mcpServers; - const claudeServers = claudeMcp.mcpServers; - - if (!isRecord(cursorServers)) { - fail("mcp.json", "`mcpServers` must be an object"); - return; - } - if (!isRecord(claudeServers)) { - fail(".mcp.json", "`mcpServers` must be an object"); - return; - } - - const cursorJson = JSON.stringify(cursorServers); - const claudeJson = JSON.stringify(claudeServers); - if (cursorJson !== claudeJson) { - fail("mcp.json", "`mcpServers` must match `.mcp.json` exactly"); - } - - const context7 = cursorServers.context7; - if (!isRecord(context7)) { - fail("mcp.json", "`mcpServers.context7` is required"); - return; - } - if (context7.url !== "https://mcp.context7.com/mcp") { - fail("mcp.json", "`mcpServers.context7.url` must point at Context7"); - } -} - /** * Hosts decide whether to update by comparing this string to what is already * installed, so a disagreement means some hosts update and others silently do @@ -240,7 +207,6 @@ function main(): void { validateCursorMarketplace(); validateClaudePlugin(); validateClaudeMarketplace(); - validateMcpConfigs(); validateVersions(); if (issues.length === 0) { diff --git a/tests/plugin-manifests.sh b/tests/plugin-manifests.sh index 5901d72..b59abeb 100755 --- a/tests/plugin-manifests.sh +++ b/tests/plugin-manifests.sh @@ -139,17 +139,6 @@ want="$(tr ' ' '\n' <<< "$SHIPPED" | sort | tr '\n' ' ')" && ok "$(printf '%-48s %s' "only client-facing agents are shipped" "$(wc -w <<< "$want" | tr -d ' ') of them")" \ || bad "agents/ has changed" "shipped='${have% }' expected='${want% }' — build roles belong in tools/agents/" -# ------------------------------------------------------------------- the MCP files -# Two names for one file because the hosts look in different places. They drifted -# once; a client on one host would then get doc fetching the other host's client does not. -if [[ -f mcp.json && -f .mcp.json ]]; then - cmp -s mcp.json .mcp.json \ - && ok "mcp.json and .mcp.json are identical" \ - || bad "the two MCP files have drifted" "one host would get different servers" -else - bad "an MCP file is missing" "mcp.json and .mcp.json must both exist" -fi - # ---------------------------------------------------------------- the host's paths # Everything below is read verbatim by a client's agent. An absolute path from this # machine in it is an instruction to use a directory the client does not have — and it diff --git a/tools/install-local b/tools/install-local index 6ab4c32..fba0223 100755 --- a/tools/install-local +++ b/tools/install-local @@ -249,15 +249,13 @@ for s in iterable-provision iterable-android iterable-android-verify iterable-io check "$s/SKILL.md" done check .claude-plugin/plugin.json -check mcp.json -check .mcp.json check agents/ios-provisioner.md for c in onboard wizard gates agent provision teardown discover iterable-keys proof-push handoff config.sh ios-provision ios-prepare-proof ios-install ios-launch ios-observe ios-send ios-deliver ios-verify; do [[ -x "$DEST/bin/$c" || "$c" == config.sh ]] || MISSING="$MISSING bin/$c(not executable)" check "bin/$c" done [[ -z "$MISSING" ]] || die "the build is missing:$MISSING" -say "six skills, the iOS specialist, both MCP files, and every entry point present and executable" +say "six skills, the iOS specialist, and every entry point present and executable" INSTALLED_NAME="$(node -e 'process.stdout.write(require(process.argv[1]).name)' "$MKT_DIR/.claude-plugin/marketplace.json")" [[ "$INSTALLED_NAME" == "$MKT" ]] || die "the marketplace still calls itself '$INSTALLED_NAME'"