forgemesh-imagegen 1.0.0 → 1.0.1

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 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
@@ -16,6 +16,7 @@ Part of the [ForgeMesh](https://github.com/forgemeshlabs/forgemesh) ecosystem
16
16
  | `generate_pro` | Generate + bg removal + 4x upscale | $0.30 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
  }
@@ -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,9 +8,23 @@ 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: 100000n },
27
+ };
14
28
 
15
29
  const TOOLS = [
16
30
  {
@@ -21,6 +35,7 @@ const TOOLS = [
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
  },
@@ -82,14 +97,63 @@ function buildHttpClient() {
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.1" },
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": "1.0.1",
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",
@@ -41,11 +41,11 @@
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
51
  "@modelcontextprotocol/sdk": "^1.10.1",