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.
Files changed (4) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +14 -9
  3. package/index.js +83 -18
  4. package/package.json +6 -6
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 CoinOpAI
3
+ Copyright (c) 2026 ForgeMesh Labs
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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` | Text-to-image generation | $0.10 USDC |
14
- | `generate_clean` | Generate + background removal | $0.15 USDC |
15
- | `generate_hd` | Generate + 4x upscale (HD) | $0.20 USDC |
16
- | `generate_pro` | Generate + bg removal + 4x upscale | $0.30 USDC |
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 ≈ 10 base images, 6 clean, 5 HD, or 3 pro
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
- - Facilitator: Coinbase CDP
94
+ - Protocol: [x402](https://x402.org)
90
95
 
91
96
  ---
92
97
 
93
98
  ## Links
94
99
 
95
- - [ForgeMesh](https://forgemesh.io) — ecosystem overview
96
- - [GitHub](https://github.com/forgemeshlabs/forgemesh-imagegen)
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 an AI image from a text prompt. Returns a PNG image URL. Costs $0.10 USDC on Base mainnet — paid automatically.",
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 the background removed. Returns a transparent PNG URL. Costs $0.15 USDC on Base mainnet.",
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 an AI image upscaled to 4x resolution. Returns a high-resolution image URL. Costs $0.20 USDC on Base mainnet.",
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 an AI image with background removed AND upscaled 4x HD. The full pipeline. Costs $0.30 USDC on Base mainnet.",
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.10" },
68
- generate_clean: { path: "/generate/clean", price: "$0.15" },
69
- generate_hd: { path: "/generate/hd", price: "$0.20" },
70
- generate_pro: { path: "/generate/pro", price: "$0.30" },
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.10 | generate_clean = $0.15 | generate_hd = $0.20 | generate_pro = $0.30"
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
- async function callPaid(httpClient, path, params) {
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 res = await fetch(url.toString());
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 httpClient;
184
+ let ctx;
121
185
  try {
122
- httpClient = buildHttpClient();
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.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 data = await callPaid(httpClient, endpoint.path, {
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": "1.0.0",
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"
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/forgemesh-imagegen.git"
44
+ "url": "https://github.com/forgemeshlabs/imagegen-mcp.git"
45
45
  },
46
- "homepage": "https://github.com/forgemeshlabs/forgemesh-imagegen",
46
+ "homepage": "https://github.com/forgemeshlabs/imagegen-mcp",
47
47
  "bugs": {
48
- "url": "https://github.com/forgemeshlabs/forgemesh-imagegen/issues"
48
+ "url": "https://github.com/forgemeshlabs/imagegen-mcp/issues"
49
49
  },
50
50
  "dependencies": {
51
- "@modelcontextprotocol/sdk": "^1.10.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"