forgemesh-imagegen 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +14 -9
- package/index.js +83 -18
- package/package.json +6 -6
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -10,12 +10,13 @@ Part of the [ForgeMesh](https://github.com/forgemeshlabs/forgemesh) ecosystem
|
|
|
10
10
|
|
|
11
11
|
| Tool | What it does | Price |
|
|
12
12
|
|---|---|---|
|
|
13
|
-
| `generate_image` |
|
|
14
|
-
| `generate_clean` | Generate + background removal | $0.
|
|
15
|
-
| `generate_hd` |
|
|
16
|
-
| `generate_pro` |
|
|
13
|
+
| `generate_image` | Standard text-to-image generation | $0.25 USDC |
|
|
14
|
+
| `generate_clean` | Generate + background removal | $0.35 USDC |
|
|
15
|
+
| `generate_hd` | Premium generate + 4x upscale (HD) | $0.50 USDC |
|
|
16
|
+
| `generate_pro` | Top-tier generate + bg removal + 4x upscale | $0.75 USDC |
|
|
17
17
|
|
|
18
18
|
All tools accept `prompt` (required) and `aspect` (optional: `1:1`, `16:9`, `9:16`, `4:3`).
|
|
19
|
+
`generate_image` also accepts `affiliate_id` for Pyrimid attribution, or uses `PYRIMID_AFFILIATE_ID` from the environment.
|
|
19
20
|
|
|
20
21
|
---
|
|
21
22
|
|
|
@@ -32,7 +33,8 @@ Add to `claude_desktop_config.json`:
|
|
|
32
33
|
"command": "npx",
|
|
33
34
|
"args": ["-y", "forgemesh-imagegen"],
|
|
34
35
|
"env": {
|
|
35
|
-
"WALLET_PRIVATE_KEY": "0x..."
|
|
36
|
+
"WALLET_PRIVATE_KEY": "0x...",
|
|
37
|
+
"PYRIMID_AFFILIATE_ID": "af_your_id"
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
}
|
|
@@ -55,7 +57,7 @@ export WALLET_PRIVATE_KEY=0x...
|
|
|
55
57
|
## Requirements
|
|
56
58
|
|
|
57
59
|
- A Base mainnet wallet private key with USDC
|
|
58
|
-
- $1 USDC ≈
|
|
60
|
+
- $1 USDC ≈ 4 base images, 2 clean, 2 HD, or 1 pro
|
|
59
61
|
- No other API keys needed
|
|
60
62
|
|
|
61
63
|
Get USDC on Base: [Coinbase](https://coinbase.com) → Bridge to Base, or buy directly on Base.
|
|
@@ -68,6 +70,9 @@ Get USDC on Base: [Coinbase](https://coinbase.com) → Bridge to Base, or buy di
|
|
|
68
70
|
generate_image(prompt="a red panda in a spacesuit", aspect="1:1")
|
|
69
71
|
→ { image_url: "https://...", prompt: "...", aspect: "1:1", tier: "image" }
|
|
70
72
|
|
|
73
|
+
generate_image(prompt="a red panda in a spacesuit", affiliate_id="af_your_id")
|
|
74
|
+
→ routes through Pyrimid product 3 with no extra cost to the caller
|
|
75
|
+
|
|
71
76
|
generate_clean(prompt="a product photo of a ceramic mug")
|
|
72
77
|
→ { image_url: "https://...", tier: "clean" } // transparent PNG
|
|
73
78
|
|
|
@@ -86,14 +91,14 @@ Each tool call makes an HTTP request to the ForgeMesh imagegen service gated by
|
|
|
86
91
|
|
|
87
92
|
- Network: Base mainnet (eip155:8453)
|
|
88
93
|
- Token: USDC
|
|
89
|
-
-
|
|
94
|
+
- Protocol: [x402](https://x402.org)
|
|
90
95
|
|
|
91
96
|
---
|
|
92
97
|
|
|
93
98
|
## Links
|
|
94
99
|
|
|
95
|
-
- [ForgeMesh](https://forgemesh
|
|
96
|
-
- [
|
|
100
|
+
- [ForgeMesh](https://github.com/forgemeshlabs/forgemesh) — ecosystem overview
|
|
101
|
+
- [npm](https://www.npmjs.com/package/forgemesh-imagegen)
|
|
97
102
|
- [x402 Protocol](https://x402.org)
|
|
98
103
|
|
|
99
104
|
---
|
package/index.js
CHANGED
|
@@ -8,26 +8,41 @@ const { x402Client, x402HTTPClient } = require("@x402/core/client");
|
|
|
8
8
|
const { ExactEvmScheme } = require("@x402/evm/exact/client");
|
|
9
9
|
const { toClientEvmSigner } = require("@x402/evm");
|
|
10
10
|
const { privateKeyToAccount } = require("viem/accounts");
|
|
11
|
+
const { createPublicClient, createWalletClient, http, parseAbi } = require("viem");
|
|
12
|
+
const { base } = require("viem/chains");
|
|
11
13
|
|
|
12
14
|
const BASE_URL = "https://imagegen.coinopai.com";
|
|
13
15
|
const VALID_ASPECTS = ["1:1", "16:9", "9:16", "4:3"];
|
|
16
|
+
const PYRIMID_ROUTER = "0xc949AEa380D7b7984806143ddbfE519B03ABd68B";
|
|
17
|
+
const PYRIMID_VENDOR_ID = "0x034604e25078e293d7b181fa23b3f2f6";
|
|
18
|
+
const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
19
|
+
const ROUTER_ABI = parseAbi([
|
|
20
|
+
"function routePayment(bytes16 vendorId, uint256 productId, bytes16 affiliateId, address buyer, uint256 maxPrice) external",
|
|
21
|
+
]);
|
|
22
|
+
const USDC_ABI = parseAbi([
|
|
23
|
+
"function approve(address spender, uint256 amount) external returns (bool)",
|
|
24
|
+
]);
|
|
25
|
+
const PYRIMID_PRODUCTS = {
|
|
26
|
+
"/generate": { productId: 3n, priceUsdc: 250000n },
|
|
27
|
+
};
|
|
14
28
|
|
|
15
29
|
const TOOLS = [
|
|
16
30
|
{
|
|
17
31
|
name: "generate_image",
|
|
18
|
-
description: "Generate
|
|
32
|
+
description: "Generate a standard AI image from a text prompt. Returns a PNG image URL. Costs $0.25 USDC on Base mainnet — paid automatically.",
|
|
19
33
|
inputSchema: {
|
|
20
34
|
type: "object",
|
|
21
35
|
properties: {
|
|
22
36
|
prompt: { type: "string", description: "Text description of the image to generate" },
|
|
23
37
|
aspect: { type: "string", enum: VALID_ASPECTS, description: "Aspect ratio — 1:1 (default), 16:9, 9:16, 4:3" },
|
|
38
|
+
affiliate_id: { type: "string", description: "Optional Pyrimid affiliate ID. Affiliate earns a commission from within the listed price — no extra cost to you." },
|
|
24
39
|
},
|
|
25
40
|
required: ["prompt"],
|
|
26
41
|
},
|
|
27
42
|
},
|
|
28
43
|
{
|
|
29
44
|
name: "generate_clean",
|
|
30
|
-
description: "Generate an AI image with
|
|
45
|
+
description: "Generate an AI image with background removed. Returns a transparent PNG URL. Costs $0.35 USDC on Base mainnet.",
|
|
31
46
|
inputSchema: {
|
|
32
47
|
type: "object",
|
|
33
48
|
properties: {
|
|
@@ -39,7 +54,7 @@ const TOOLS = [
|
|
|
39
54
|
},
|
|
40
55
|
{
|
|
41
56
|
name: "generate_hd",
|
|
42
|
-
description: "Generate
|
|
57
|
+
description: "Generate a premium AI image upscaled 4x HD. Returns a high-resolution image URL. Costs $0.50 USDC on Base mainnet.",
|
|
43
58
|
inputSchema: {
|
|
44
59
|
type: "object",
|
|
45
60
|
properties: {
|
|
@@ -51,7 +66,7 @@ const TOOLS = [
|
|
|
51
66
|
},
|
|
52
67
|
{
|
|
53
68
|
name: "generate_pro",
|
|
54
|
-
description: "Generate
|
|
69
|
+
description: "Generate a top-tier AI image with background removal and 4x HD upscale. Costs $0.75 USDC on Base mainnet.",
|
|
55
70
|
inputSchema: {
|
|
56
71
|
type: "object",
|
|
57
72
|
properties: {
|
|
@@ -64,10 +79,10 @@ const TOOLS = [
|
|
|
64
79
|
];
|
|
65
80
|
|
|
66
81
|
const TOOL_ENDPOINTS = {
|
|
67
|
-
generate_image: { path: "/generate", price: "$0.
|
|
68
|
-
generate_clean: { path: "/generate/clean", price: "$0.
|
|
69
|
-
generate_hd: { path: "/generate/hd", price: "$0.
|
|
70
|
-
generate_pro: { path: "/generate/pro", price: "$0.
|
|
82
|
+
generate_image: { path: "/generate", price: "$0.25" },
|
|
83
|
+
generate_clean: { path: "/generate/clean", price: "$0.35" },
|
|
84
|
+
generate_hd: { path: "/generate/hd", price: "$0.50" },
|
|
85
|
+
generate_pro: { path: "/generate/pro", price: "$0.75" },
|
|
71
86
|
};
|
|
72
87
|
|
|
73
88
|
function buildHttpClient() {
|
|
@@ -75,21 +90,70 @@ function buildHttpClient() {
|
|
|
75
90
|
if (!key) {
|
|
76
91
|
throw new Error(
|
|
77
92
|
"WALLET_PRIVATE_KEY required — set a Base wallet private key funded with USDC.\n" +
|
|
78
|
-
" generate_image = $0.
|
|
93
|
+
" generate_image = $0.25 | generate_clean = $0.35 | generate_hd = $0.50 | generate_pro = $0.75"
|
|
79
94
|
);
|
|
80
95
|
}
|
|
81
96
|
const pk = key.startsWith("0x") ? key : "0x" + key;
|
|
82
97
|
const account = privateKeyToAccount(pk);
|
|
83
98
|
const signer = toClientEvmSigner(account);
|
|
84
99
|
const coreClient = new x402Client().register("eip155:*", new ExactEvmScheme(signer));
|
|
85
|
-
return new x402HTTPClient(coreClient);
|
|
100
|
+
return { httpClient: new x402HTTPClient(coreClient), account };
|
|
86
101
|
}
|
|
87
102
|
|
|
88
|
-
|
|
103
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
104
|
+
|
|
105
|
+
async function callPyrimid(account, url, product, affiliateId) {
|
|
106
|
+
const transport = http();
|
|
107
|
+
const publicClient = createPublicClient({ chain: base, transport });
|
|
108
|
+
const walletClient = createWalletClient({ account, chain: base, transport });
|
|
109
|
+
|
|
110
|
+
const approveHash = await walletClient.writeContract({
|
|
111
|
+
address: USDC_ADDRESS,
|
|
112
|
+
abi: USDC_ABI,
|
|
113
|
+
functionName: "approve",
|
|
114
|
+
args: [PYRIMID_ROUTER, product.priceUsdc],
|
|
115
|
+
});
|
|
116
|
+
await publicClient.waitForTransactionReceipt({ hash: approveHash });
|
|
117
|
+
await sleep(3000);
|
|
118
|
+
|
|
119
|
+
const routeHash = await walletClient.writeContract({
|
|
120
|
+
address: PYRIMID_ROUTER,
|
|
121
|
+
abi: ROUTER_ABI,
|
|
122
|
+
functionName: "routePayment",
|
|
123
|
+
args: [PYRIMID_VENDOR_ID, product.productId, "0x00000000000000000000000000000000", account.address, product.priceUsdc],
|
|
124
|
+
});
|
|
125
|
+
await publicClient.waitForTransactionReceipt({ hash: routeHash });
|
|
126
|
+
await sleep(3000);
|
|
127
|
+
|
|
128
|
+
const paidRes = await fetch(url.toString(), {
|
|
129
|
+
headers: { "X-Affiliate-ID": affiliateId, "X-Payment": routeHash },
|
|
130
|
+
});
|
|
131
|
+
if (!paidRes.ok) {
|
|
132
|
+
const errBody = await paidRes.text().catch(() => paidRes.statusText);
|
|
133
|
+
throw new Error(`Pyrimid payment failed — HTTP ${paidRes.status}: ${errBody.slice(0, 200)}`);
|
|
134
|
+
}
|
|
135
|
+
return paidRes.json();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function callPaid(ctx, path, params, affiliateId) {
|
|
139
|
+
const { httpClient, account } = ctx;
|
|
89
140
|
const url = new URL(BASE_URL + path);
|
|
90
141
|
if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
91
142
|
|
|
92
|
-
const
|
|
143
|
+
const product = PYRIMID_PRODUCTS[path];
|
|
144
|
+
if (!product) affiliateId = null;
|
|
145
|
+
const extraHeaders = affiliateId ? { "X-Affiliate-ID": affiliateId } : {};
|
|
146
|
+
|
|
147
|
+
if (affiliateId && product) {
|
|
148
|
+
try {
|
|
149
|
+
return await callPyrimid(account, url, product, affiliateId);
|
|
150
|
+
} catch (_) {
|
|
151
|
+
affiliateId = null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const directHeaders = affiliateId ? extraHeaders : {};
|
|
156
|
+
const res = await fetch(url.toString(), { headers: directHeaders });
|
|
93
157
|
|
|
94
158
|
if (res.status === 402) {
|
|
95
159
|
let body;
|
|
@@ -100,7 +164,7 @@ async function callPaid(httpClient, path, params) {
|
|
|
100
164
|
);
|
|
101
165
|
const paymentPayload = await httpClient.createPaymentPayload(paymentRequired);
|
|
102
166
|
const paidRes = await fetch(url.toString(), {
|
|
103
|
-
headers: httpClient.encodePaymentSignatureHeader(paymentPayload),
|
|
167
|
+
headers: { ...httpClient.encodePaymentSignatureHeader(paymentPayload), ...directHeaders },
|
|
104
168
|
});
|
|
105
169
|
if (!paidRes.ok) {
|
|
106
170
|
const errBody = await paidRes.text().catch(() => paidRes.statusText);
|
|
@@ -117,16 +181,16 @@ async function callPaid(httpClient, path, params) {
|
|
|
117
181
|
}
|
|
118
182
|
|
|
119
183
|
async function main() {
|
|
120
|
-
let
|
|
184
|
+
let ctx;
|
|
121
185
|
try {
|
|
122
|
-
|
|
186
|
+
ctx = buildHttpClient();
|
|
123
187
|
} catch (e) {
|
|
124
188
|
process.stderr.write("[forgemesh-imagegen] " + e.message + "\n");
|
|
125
189
|
process.exit(1);
|
|
126
190
|
}
|
|
127
191
|
|
|
128
192
|
const server = new Server(
|
|
129
|
-
{ name: "forgemesh-imagegen", version: "1.0.
|
|
193
|
+
{ name: "forgemesh-imagegen", version: "1.0.2" },
|
|
130
194
|
{ capabilities: { tools: {} } }
|
|
131
195
|
);
|
|
132
196
|
|
|
@@ -147,10 +211,11 @@ async function main() {
|
|
|
147
211
|
throw new Error(`Invalid aspect ratio '${aspect}'. Valid: ${VALID_ASPECTS.join(", ")}`);
|
|
148
212
|
}
|
|
149
213
|
|
|
150
|
-
const
|
|
214
|
+
const affiliateId = args.affiliate_id || process.env.PYRIMID_AFFILIATE_ID || null;
|
|
215
|
+
const data = await callPaid(ctx, endpoint.path, {
|
|
151
216
|
prompt: args.prompt.trim(),
|
|
152
217
|
aspect,
|
|
153
|
-
});
|
|
218
|
+
}, affiliateId);
|
|
154
219
|
|
|
155
220
|
return {
|
|
156
221
|
content: [{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forgemesh-imagegen",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"mcpName": "io.github.forgemeshlabs/imagegen-mcp",
|
|
5
5
|
"description": "MCP server for AI image generation — generate, clean (bg removal), HD (4x upscale), Pro. Pay per image in USDC via x402 on Base. No API key needed.",
|
|
6
6
|
"main": "index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"start": "node index.js"
|
|
16
16
|
},
|
|
17
17
|
"engines": {
|
|
18
|
-
"node": ">=
|
|
18
|
+
"node": ">=20"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
21
|
"mcp",
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"repository": {
|
|
43
43
|
"type": "git",
|
|
44
|
-
"url": "https://github.com/forgemeshlabs/
|
|
44
|
+
"url": "https://github.com/forgemeshlabs/imagegen-mcp.git"
|
|
45
45
|
},
|
|
46
|
-
"homepage": "https://github.com/forgemeshlabs/
|
|
46
|
+
"homepage": "https://github.com/forgemeshlabs/imagegen-mcp",
|
|
47
47
|
"bugs": {
|
|
48
|
-
"url": "https://github.com/forgemeshlabs/
|
|
48
|
+
"url": "https://github.com/forgemeshlabs/imagegen-mcp/issues"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
52
52
|
"@x402/core": "^2.11.0",
|
|
53
53
|
"@x402/evm": "^2.11.0",
|
|
54
54
|
"viem": "^2.0.0"
|