From ad53b92ef4ee34285ad5603c67c0b0e58443f211 Mon Sep 17 00:00:00 2001 From: Dmitry Sergeev Date: Sun, 15 Nov 2026 14:31:16 +0100 Subject: [PATCH] chore: add a Cursor manifest to the plugin Cursor reads a plugin from .cursor-plugin/plugin.json and finds it in a repository through .cursor-plugin/marketplace.json at the root. The new manifest points at the same skill folder and reuses the listing icon of the Claude Code plugin. Tests keep its version, name and description in step with the package and the Claude Code manifest, and the metadata check covers its links. The plugin README now says "a coding agent" where it said "Claude". --- .cursor-plugin/marketplace.json | 12 ++++++ .../.cursor-plugin/plugin.json | 15 +++++++ plugins/agent-commerce-gateway/README.md | 4 +- scripts/check-package-metadata.mjs | 6 ++- tests/unit/cli/skill.test.ts | 43 +++++++++++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 .cursor-plugin/marketplace.json create mode 100644 plugins/agent-commerce-gateway/.cursor-plugin/plugin.json diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json new file mode 100644 index 0000000..860c199 --- /dev/null +++ b/.cursor-plugin/marketplace.json @@ -0,0 +1,12 @@ +{ + "name": "agent-commerce", + "owner": { "name": "devlab-group" }, + "metadata": { "description": "The Agent Commerce Gateway skill for coding agents." }, + "plugins": [ + { + "name": "agent-commerce-gateway", + "source": "./plugins/agent-commerce-gateway", + "description": "Integrate the Agent Commerce Gateway into an existing HTTP API: configure, start and verify it." + } + ] +} diff --git a/plugins/agent-commerce-gateway/.cursor-plugin/plugin.json b/plugins/agent-commerce-gateway/.cursor-plugin/plugin.json new file mode 100644 index 0000000..aacd354 --- /dev/null +++ b/plugins/agent-commerce-gateway/.cursor-plugin/plugin.json @@ -0,0 +1,15 @@ +{ + "name": "agent-commerce-gateway", + "displayName": "Agent Commerce Gateway", + "version": "2.7.2", + "description": "Put the Agent Commerce Gateway in front of an existing HTTP API so AI agents can call and pay for it with x402.", + "author": { "name": "devlab-group" }, + "homepage": "https://github.com/devlab-group/agent-commerce#readme", + "repository": "https://github.com/devlab-group/agent-commerce", + "license": "Apache-2.0", + "logo": ".claude-plugin/icon.png", + "keywords": ["x402", "mcp", "ai-agents", "payments", "agentic-commerce"], + "category": "developer-tools", + "tags": ["x402", "payments", "mcp"], + "skills": "./skills/" +} diff --git a/plugins/agent-commerce-gateway/README.md b/plugins/agent-commerce-gateway/README.md index 2b5a6b1..c12dea8 100644 --- a/plugins/agent-commerce-gateway/README.md +++ b/plugins/agent-commerce-gateway/README.md @@ -1,6 +1,6 @@ # Agent Commerce Gateway plugin -This plugin adds one skill, `agent-commerce`, that helps Claude put +This plugin adds one skill, `agent-commerce`, that helps a coding agent put [Agent Commerce Gateway](https://github.com/devlab-group/agent-commerce) (npm `@devlab.group/agent-commerce`) in front of an existing HTTP API. AI agents can then call the API's endpoints over HTTP or MCP and pay for them @@ -37,7 +37,7 @@ anywhere. ## Credentials the gateway uses The skill writes credentials only as `${VAR}` placeholders in the gateway's -`config.yaml`, and tells Claude never to ask for a secret in chat or write one +`config.yaml`, and tells the agent never to ask for a secret in chat or write one into a file, command or log. The user sets the values in the gateway's own `.env`, which the gateway CLI reads when it runs. Each value goes only to the party it belongs to: diff --git a/scripts/check-package-metadata.mjs b/scripts/check-package-metadata.mjs index a3408c4..32cc3c7 100644 --- a/scripts/check-package-metadata.mjs +++ b/scripts/check-package-metadata.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node /** - * Require package and Claude Code plugin links to point at the real repository. + * Require package, Claude Code plugin and Cursor plugin links to point at the real repository. * A wrong namespace could send private vulnerability reports to an attacker. * An allowlist rejects destinations a placeholder blocklist would miss. */ @@ -13,6 +13,8 @@ const manifestPath = resolve(root, 'package.json'); const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); const pluginManifest = 'plugins/agent-commerce-gateway/.claude-plugin/plugin.json'; const plugin = JSON.parse(readFileSync(resolve(root, pluginManifest), 'utf8')); +const cursorManifest = 'plugins/agent-commerce-gateway/.cursor-plugin/plugin.json'; +const cursor = JSON.parse(readFileSync(resolve(root, cursorManifest), 'utf8')); const EXPECTED = 'github.com/devlab-group/agent-commerce'; @@ -24,6 +26,8 @@ const fields = [ [`${pluginManifest} homepage`, plugin.homepage], [`${pluginManifest} documentationUrl`, plugin.documentationUrl], [`${pluginManifest} supportUrl`, plugin.supportUrl], + [`${cursorManifest} repository`, cursor.repository], + [`${cursorManifest} homepage`, cursor.homepage], ]; // Anchored at the scheme and host, because npm renders these as links: a diff --git a/tests/unit/cli/skill.test.ts b/tests/unit/cli/skill.test.ts index b8b05ce..c211fb4 100644 --- a/tests/unit/cli/skill.test.ts +++ b/tests/unit/cli/skill.test.ts @@ -261,3 +261,46 @@ describe('Claude Code plugin', () => { expect(existsSync(join(pluginDir, 'package.json'))).toBe(false); }); }); + +describe('Cursor plugin', () => { + const plugin = readJson<{ + name: string; + displayName: string; + version: string; + description: string; + license: string; + logo: string; + skills: string; + }>(join(pluginDir, '.cursor-plugin', 'plugin.json')); + const claude = readJson<{ name: string; displayName: string; description: string }>( + join(pluginDir, '.claude-plugin', 'plugin.json'), + ); + const marketplace = readJson<{ name: string; plugins: { name: string; source: string }[] }>( + join(root, '.cursor-plugin', 'marketplace.json'), + ); + + it('releases with the package version and matches the Claude Code plugin', () => { + expect(plugin.version).toBe(manifest.version); + expect(plugin.license).toBe(manifest.license); + expect(plugin).toMatchObject({ + name: claude.name, + displayName: claude.displayName, + description: claude.description, + }); + }); + + it('is listed in the marketplace from the same folder', () => { + expect(marketplace.plugins).toEqual([ + expect.objectContaining({ name: plugin.name, source: './plugins/agent-commerce-gateway' }), + ]); + }); + + it('points at files inside the plugin folder', () => { + // Cursor requires relative manifest paths without `..` + for (const path of [plugin.logo, plugin.skills]) { + expect(path).not.toMatch(/^\/|^[a-z]+:|\.\./); + expect(existsSync(join(pluginDir, path))).toBe(true); + } + expect(existsSync(join(pluginDir, plugin.skills, 'agent-commerce', 'SKILL.md'))).toBe(true); + }); +});