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
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"description": "The Agent Commerce Gateway skill for coding agents.",
"plugins": [
{
"name": "agent-commerce",
"source": ".",
"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."
}
]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ claims and pending-task takes, then check that exactly one caller wins.
## Integrate with an AI coding agent

The repository includes an experimental [Agent Skills](https://agentskills.io)
skill, [`skills/agent-commerce`](skills/agent-commerce/SKILL.md), for adding the
skill, [`agent-commerce`](plugins/agent-commerce-gateway/skills/agent-commerce/SKILL.md), for adding the
gateway to an existing API. It covers Base Sepolia configuration, startup and
checks through an unpaid 402 challenge. Its instructions keep secrets out of
chat and leave payment signing to the user. The skill has automated CLI and
Expand All @@ -188,7 +188,7 @@ config checks.
```text
# Claude Code
/plugin marketplace add devlab-group/agent-commerce
/plugin install agent-commerce@agent-commerce
/plugin install agent-commerce-gateway@agent-commerce

# Codex, Cursor and other agents (a third-party installer)
DISABLE_TELEMETRY=1 npx skills add devlab-group/agent-commerce --skill agent-commerce
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
{
"name": "agent-commerce",
"name": "agent-commerce-gateway",
"displayName": "Agent Commerce Gateway",
"version": "2.6.0",
"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", "url": "https://github.com/devlab-group" },
"homepage": "https://github.com/devlab-group/agent-commerce#readme",
"repository": "https://github.com/devlab-group/agent-commerce",
"documentationUrl": "https://github.com/devlab-group/agent-commerce#readme",
"supportUrl": "https://github.com/devlab-group/agent-commerce/issues",
"license": "Apache-2.0",
"keywords": ["x402", "mcp", "ai-agents", "payments", "agentic-commerce"]
}
61 changes: 61 additions & 0 deletions plugins/agent-commerce-gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Agent Commerce Gateway plugin

This plugin adds one skill, `agent-commerce`, that helps Claude 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
with x402 stablecoin payments, which go straight to the merchant's wallet.

## What the skill does

- Reads the API project in the working directory (its stack, routes and any
OpenAPI file), or asks for the API's base URL and endpoints.
- Asks which endpoints agents may call, which of them are paid and at what
price in USDC, the merchant wallet address, and how to keep callers from
bypassing the gateway. It never chooses prices.
- Creates an `agent-commerce/` directory with its own `package.json`, installs
the gateway there, and writes `config.yaml` and `.env.example`.
- Runs the gateway's `validate`, `start` and `doctor` commands, and checks that
an unpaid call to a paid endpoint returns HTTP 402.

## What it runs, sends and fetches

- `npm install @devlab.group/agent-commerce`, and the optional peer packages
the configuration enables (such as `@x402/core`, `@x402/evm`, `viem` and
`@modelcontextprotocol/server`), from the npm registry.
- The locally installed gateway CLI through `npx --no -- agent-commerce`,
which refuses to download a missing package.
- `curl` requests to the local gateway, by default on `127.0.0.1:8080`.
- The gateway it configures calls the merchant's API, an x402 facilitator
(by default `https://x402.org/facilitator`) and a chain RPC endpoint (by
default `https://base-sepolia-rpc.publicnode.com`, the Base Sepolia
testnet).

The plugin has no hooks, MCP servers or scripts of its own, and sends no data
anywhere.

## Safety rules in the skill

- It never asks for a secret in chat or writes one into a file, command or
log. Secrets are `${VAR}` placeholders that the user fills in `.env`.
- It never generates, requests or handles a private key, seed phrase or
wallet.
- It asks first before changing application code, overwriting or deleting
files, deploying, or touching mainnet.

## Requirements

Node.js 22 or later, npm 10 or later, and `@devlab.group/agent-commerce`
2.2.0 or later.

## Install in Claude Code

```text
/plugin marketplace add devlab-group/agent-commerce
/plugin install agent-commerce-gateway@agent-commerce
```

## License

Apache-2.0. Source and documentation:
https://github.com/devlab-group/agent-commerce
9 changes: 6 additions & 3 deletions scripts/check-package-metadata.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ import { fileURLToPath } from 'node:url';
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const manifestPath = resolve(root, 'package.json');
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
const plugin = JSON.parse(readFileSync(resolve(root, '.claude-plugin', 'plugin.json'), 'utf8'));
const pluginManifest = 'plugins/agent-commerce-gateway/.claude-plugin/plugin.json';
const plugin = JSON.parse(readFileSync(resolve(root, pluginManifest), 'utf8'));

const EXPECTED = 'github.com/devlab-group/agent-commerce';

const fields = [
['repository.url', manifest.repository?.url],
['bugs.url', manifest.bugs?.url],
['homepage', manifest.homepage],
['.claude-plugin/plugin.json repository', plugin.repository],
['.claude-plugin/plugin.json homepage', plugin.homepage],
[`${pluginManifest} repository`, plugin.repository],
[`${pluginManifest} homepage`, plugin.homepage],
[`${pluginManifest} documentationUrl`, plugin.documentationUrl],
[`${pluginManifest} supportUrl`, plugin.supportUrl],
];

// Anchored at the scheme and host, because npm renders these as links: a
Expand Down
15 changes: 10 additions & 5 deletions tests/unit/cli/skill.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import { RawConfigSchema } from '../../../src/config/schema/raw';
import { createCapturingIo } from './fixtures';

const root = resolve(dirname(fileURLToPath(import.meta.url)), '../../..');
const skillDir = join(root, 'skills', 'agent-commerce');
const pluginDir = join(root, 'plugins', 'agent-commerce-gateway');
const skillDir = join(pluginDir, 'skills', 'agent-commerce');
const readJson = <T>(path: string): T => JSON.parse(readFileSync(path, 'utf8')) as T;
const manifest = readJson<{ name: string; version: string; license: string }>(
join(root, 'package.json'),
Expand Down Expand Up @@ -234,7 +235,7 @@ describe('agent skill', () => {

describe('Claude Code plugin', () => {
const plugin = readJson<{ name: string; version: string; license: string }>(
join(root, '.claude-plugin', 'plugin.json'),
join(pluginDir, '.claude-plugin', 'plugin.json'),
);
const marketplace = readJson<{ name: string; plugins: { name: string; source: string }[] }>(
join(root, '.claude-plugin', 'marketplace.json'),
Expand All @@ -246,10 +247,14 @@ describe('Claude Code plugin', () => {
expect(plugin.license).toBe(manifest.license);
});

it('is listed in the marketplace from the repository root, beside the skill', () => {
it('is listed in the marketplace from its own folder, which holds the skill', () => {
// Installs copy only the plugin folder: the skill must be inside it, and
// the repository's package.json and lockfile must stay outside it
expect(marketplace.plugins).toEqual([
expect.objectContaining({ name: plugin.name, source: '.' }),
expect.objectContaining({ name: plugin.name, source: './plugins/agent-commerce-gateway' }),
]);
expect(existsSync(join(root, 'skills', plugin.name, 'SKILL.md'))).toBe(true);
expect(existsSync(join(skillDir, 'SKILL.md'))).toBe(true);
expect(existsSync(join(pluginDir, 'README.md'))).toBe(true);
expect(existsSync(join(pluginDir, 'package.json'))).toBe(false);
});
});
Loading