mint-day 0.4.1 → 0.4.3

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/dist/index.js CHANGED
@@ -11,11 +11,6 @@ import { mintSchema, handleMint } from "./tools/mint.js";
11
11
  import { mintCheckSchema, handleMintCheck } from "./tools/mint-check.js";
12
12
  import { mintResolveSchema, handleMintResolve } from "./tools/mint-resolve.js";
13
13
  dotenv.config();
14
- // Testnet try-it wallet (Base Sepolia, pre-funded)
15
- const TRYIT_KEY = "";
16
- const TRYIT_CONTRACT = "0xa52450397f312c256Bd68B202C0CF90387Ea0E67";
17
- const TRYIT_RPC = "https://sepolia.base.org";
18
- const TRYIT_CHAIN = 84532;
19
14
  const MAINNET_CONTRACT = "0x12a1c11a0b2860f64e7d8df20989f97d40de7f2c";
20
15
  const MAINNET_RPC = "https://mainnet.base.org";
21
16
  const MAINNET_CHAIN = 8453;
@@ -33,7 +28,7 @@ function loadPrivateKey() {
33
28
  }
34
29
  const userKey = loadPrivateKey();
35
30
  const hasUserKey = !!userKey;
36
- // Mainnet by default. Testnet only when no private key AND no recipient provided (decided per-call).
31
+ // Mainnet configuration
37
32
  const chainId = parseInt(process.env.CHAIN_ID || String(hasUserKey ? MAINNET_CHAIN : MAINNET_CHAIN));
38
33
  const contractAddress = process.env.MINT_FACTORY_ADDRESS || MAINNET_CONTRACT;
39
34
  const rpcUrl = process.env.BASE_RPC_URL || MAINNET_RPC;
@@ -54,12 +49,7 @@ const server = new McpServer({
54
49
  name: "mint-day",
55
50
  version: "0.4.0",
56
51
  });
57
- server.tool("mint", "Create a permanent, verifiable on-chain record on Base. Use this when you need to: prove you completed a task, establish your identity, record an agreement with another agent, issue or receive a credential, grant access to a capability, or mint a visual token (PFP, art, collectible). Supports two protocols: 'mintday' (default, gas-sponsored) and 'superrare' (Rare Protocol, requires PRIVATE_KEY). Set platform: 'superrare' to mint on Rare Protocol contracts. The token is immutable and publicly verifiable. Provide an image URL, local file path, or base64 for visual tokens. Returns a preview with mintId first; call again with mintId to confirm.", mintSchema, async (params) => handleMint(params, calldataService, defaultRecipient, userKey, {
58
- tryitKey: TRYIT_KEY,
59
- tryitContract: TRYIT_CONTRACT,
60
- tryitRpc: TRYIT_RPC,
61
- tryitChain: TRYIT_CHAIN,
62
- }));
52
+ server.tool("mint", "Create a permanent, verifiable on-chain record on Base. Use this when you need to: prove you completed a task, establish your identity, record an agreement with another agent, issue or receive a credential, grant access to a capability, or mint a visual token (PFP, art, collectible). Supports two protocols: 'mintday' (default, gas-sponsored) and 'superrare' (Rare Protocol, requires PRIVATE_KEY). Set platform: 'superrare' to mint on Rare Protocol contracts. The token is immutable and publicly verifiable. Provide an image URL, local file path, or base64 for visual tokens. Returns a preview with mintId first; call again with mintId to confirm.", mintSchema, async (params) => handleMint(params, calldataService, defaultRecipient, userKey));
63
53
  server.tool("mint_check", "Look up mint.day tokens. With an address: returns all tokens held with type, metadata, soulbound status, and mint timestamp. Without an address: returns global stats (total minted, current fee). Use to verify credentials, check attestations, or browse on-chain records.", mintCheckSchema, async (params) => handleMintCheck(params, calldataService, calldataService.provider, contractAddress, chainId));
64
54
  server.tool("mint_resolve", "Resolve an agent's on-chain identity. Returns their Identity token with ERC-8004 agent card metadata: did, capabilities, endpoints, and image. Use this before transacting with another agent to verify who they are.", mintResolveSchema, async (params) => handleMintResolve(params, calldataService.provider, contractAddress, chainId));
65
55
  async function main() {
@@ -20,12 +20,6 @@ export declare const mintSchema: {
20
20
  mintday: "mintday";
21
21
  }>>;
22
22
  };
23
- interface TryitConfig {
24
- tryitKey: string;
25
- tryitContract: string;
26
- tryitRpc: string;
27
- tryitChain: number;
28
- }
29
23
  export declare function handleMint(params: {
30
24
  description?: string;
31
25
  tokenType?: string;
@@ -36,10 +30,9 @@ export declare function handleMint(params: {
36
30
  mintId?: string;
37
31
  metadata?: Record<string, unknown>;
38
32
  platform?: string;
39
- }, calldataService: CalldataService, defaultRecipient: string, userKey?: string, tryit?: TryitConfig): Promise<{
33
+ }, calldataService: CalldataService, defaultRecipient: string, userKey?: string): Promise<{
40
34
  content: {
41
35
  type: "text";
42
36
  text: string;
43
37
  }[];
44
38
  }>;
45
- export {};
@@ -3,7 +3,6 @@ import { z } from "zod";
3
3
  import { ethers } from "ethers";
4
4
  import { TokenType, TOKEN_TYPE_NAMES } from "../types.js";
5
5
  import { classifyIntent } from "../services/classifier.js";
6
- import { CalldataService } from "../services/calldata.js";
7
6
  import { resolveImage } from "../services/image-upload.js";
8
7
  import { mintOnRareProtocol } from "../services/rare-protocol.js";
9
8
  // Intent cache: preview mintId -> frozen intent + platform
@@ -97,19 +96,14 @@ async function sponsoredMint(result, recipient) {
97
96
  sponsored: true,
98
97
  };
99
98
  }
100
- catch {
101
- return null;
99
+ catch (err) {
100
+ const message = err instanceof Error ? err.message : String(err);
101
+ return { error: `Sponsor request failed: ${message}` };
102
102
  }
103
103
  }
104
- export async function handleMint(params, calldataService, defaultRecipient, userKey = "", tryit = { tryitKey: "", tryitContract: "", tryitRpc: "", tryitChain: 84532 }) {
105
- // Determine mode:
106
- // 1. User has private key: mainnet, sign + submit locally
107
- // 2. No key but recipient provided: mainnet, sponsored gas via server
108
- // 3. No key, no recipient: testnet try-it mode
109
- const hasRecipient = !!params.recipient;
110
- const isTestnet = !userKey && !hasRecipient;
111
- const privateKey = userKey || (isTestnet ? tryit.tryitKey : "");
104
+ export async function handleMint(params, calldataService, defaultRecipient, userKey = "") {
112
105
  // Confirmation path: replay cached intent
106
+ // Check cache first so we can use cached recipient for mode detection
113
107
  if (params.mintId) {
114
108
  const entry = getCachedEntry(params.mintId);
115
109
  if (!entry) {
@@ -123,6 +117,9 @@ export async function handleMint(params, calldataService, defaultRecipient, user
123
117
  const cached = entry.intent;
124
118
  const cachedPlatform = entry.platform;
125
119
  intentCache.delete(params.mintId);
120
+ // Mode detection using cached recipient
121
+ const hasRecipient = !!cached.recipient;
122
+ const privateKey = userKey;
126
123
  // Rare Protocol path: mint via SuperRare contracts on Base
127
124
  if (cachedPlatform === "superrare") {
128
125
  if (!privateKey) {
@@ -134,7 +131,7 @@ export async function handleMint(params, calldataService, defaultRecipient, user
134
131
  };
135
132
  }
136
133
  try {
137
- const rareResult = await mintOnRareProtocol(cached, privateKey, isTestnet);
134
+ const rareResult = await mintOnRareProtocol(cached, privateKey, false);
138
135
  return {
139
136
  content: [{
140
137
  type: "text",
@@ -152,17 +149,11 @@ export async function handleMint(params, calldataService, defaultRecipient, user
152
149
  };
153
150
  }
154
151
  }
155
- // Use testnet calldataService if in try-it mode
156
- let activeCalldataService = calldataService;
157
- if (isTestnet) {
158
- activeCalldataService = new CalldataService(tryit.tryitRpc, tryit.tryitContract, tryit.tryitChain);
159
- }
160
- const result = await activeCalldataService.buildMintCalldata(cached);
152
+ const result = await calldataService.buildMintCalldata(cached);
161
153
  // If we have a private key, sign and submit directly
162
154
  if (privateKey) {
163
155
  try {
164
- const provider = isTestnet ? activeCalldataService.provider : calldataService.provider;
165
- const wallet = new ethers.Wallet(privateKey, provider);
156
+ const wallet = new ethers.Wallet(privateKey, calldataService.provider);
166
157
  const tx = await wallet.sendTransaction({
167
158
  to: result.to,
168
159
  data: result.calldata,
@@ -178,12 +169,9 @@ export async function handleMint(params, calldataService, defaultRecipient, user
178
169
  tokenType: result.tokenType,
179
170
  soulbound: result.soulbound,
180
171
  recipient: cached.recipient,
181
- chain: isTestnet ? "Base Sepolia (testnet)" : "Base",
182
- explorer: `https://${result.chainId === 8453 ? "" : "sepolia."}basescan.org/tx/${receipt.hash}`,
172
+ chain: "Base",
173
+ explorer: `https://basescan.org/tx/${receipt.hash}`,
183
174
  };
184
- if (isTestnet) {
185
- response.note = "This token was minted on Base Sepolia (testnet). To mint on Base mainnet, save a private key to ~/.mint-day/credentials and restart the MCP server.";
186
- }
187
175
  return {
188
176
  content: [{
189
177
  type: "text",
@@ -385,12 +373,7 @@ export async function handleMint(params, calldataService, defaultRecipient, user
385
373
  preview.missingFields = intent.missingFields;
386
374
  preview.hint = "Identity tokens (ERC-8004) should include capabilities[], endpoints[], and did. Provide these in metadata for full compliance.";
387
375
  }
388
- if (isTestnet) {
389
- preview.chain = "Base Sepolia (testnet)";
390
- }
391
- else {
392
- preview.chain = "Base";
393
- }
376
+ preview.chain = "Base";
394
377
  preview.instruction = "Call mint with mintId to confirm.";
395
378
  return {
396
379
  content: [{ type: "text", text: JSON.stringify(preview, null, 2) }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mint-day",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Agent-native minting on Base. One tool call, any token type.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -13,11 +13,6 @@ import { mintResolveSchema, handleMintResolve } from "./tools/mint-resolve.js";
13
13
 
14
14
  dotenv.config();
15
15
 
16
- // Testnet try-it wallet (Base Sepolia, pre-funded)
17
- const TRYIT_KEY = "";
18
- const TRYIT_CONTRACT = "0xa52450397f312c256Bd68B202C0CF90387Ea0E67";
19
- const TRYIT_RPC = "https://sepolia.base.org";
20
- const TRYIT_CHAIN = 84532;
21
16
  const MAINNET_CONTRACT = "0x12a1c11a0b2860f64e7d8df20989f97d40de7f2c";
22
17
  const MAINNET_RPC = "https://mainnet.base.org";
23
18
  const MAINNET_CHAIN = 8453;
@@ -36,7 +31,7 @@ function loadPrivateKey(): string {
36
31
  const userKey = loadPrivateKey();
37
32
  const hasUserKey = !!userKey;
38
33
 
39
- // Mainnet by default. Testnet only when no private key AND no recipient provided (decided per-call).
34
+ // Mainnet configuration
40
35
  const chainId = parseInt(process.env.CHAIN_ID || String(hasUserKey ? MAINNET_CHAIN : MAINNET_CHAIN));
41
36
  const contractAddress = process.env.MINT_FACTORY_ADDRESS || MAINNET_CONTRACT;
42
37
  const rpcUrl = process.env.BASE_RPC_URL || MAINNET_RPC;
@@ -69,12 +64,7 @@ server.tool(
69
64
  "mint",
70
65
  "Create a permanent, verifiable on-chain record on Base. Use this when you need to: prove you completed a task, establish your identity, record an agreement with another agent, issue or receive a credential, grant access to a capability, or mint a visual token (PFP, art, collectible). Supports two protocols: 'mintday' (default, gas-sponsored) and 'superrare' (Rare Protocol, requires PRIVATE_KEY). Set platform: 'superrare' to mint on Rare Protocol contracts. The token is immutable and publicly verifiable. Provide an image URL, local file path, or base64 for visual tokens. Returns a preview with mintId first; call again with mintId to confirm.",
71
66
  mintSchema,
72
- async (params) => handleMint(params, calldataService, defaultRecipient, userKey, {
73
- tryitKey: TRYIT_KEY,
74
- tryitContract: TRYIT_CONTRACT,
75
- tryitRpc: TRYIT_RPC,
76
- tryitChain: TRYIT_CHAIN,
77
- }),
67
+ async (params) => handleMint(params, calldataService, defaultRecipient, userKey),
78
68
  );
79
69
 
80
70
  server.tool(
package/src/tools/mint.ts CHANGED
@@ -69,13 +69,6 @@ export const mintSchema = {
69
69
  .describe("Which protocol to mint on. 'mintday' uses the MintFactory contract (default). 'superrare' mints via Rare Protocol on Base."),
70
70
  };
71
71
 
72
- interface TryitConfig {
73
- tryitKey: string;
74
- tryitContract: string;
75
- tryitRpc: string;
76
- tryitChain: number;
77
- }
78
-
79
72
  const SPONSOR_URL = process.env.SPONSOR_URL || "https://www.mint.day/api/sponsor";
80
73
 
81
74
  async function sponsoredMint(result: { to: string; calldata: string; value: string; estimatedGas: number; tokenType: string; soulbound: boolean; chainId: number }, recipient: string): Promise<Record<string, unknown> | null> {
@@ -110,8 +103,9 @@ async function sponsoredMint(result: { to: string; calldata: string; value: stri
110
103
  explorer: data.explorer,
111
104
  sponsored: true,
112
105
  };
113
- } catch {
114
- return null;
106
+ } catch (err) {
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ return { error: `Sponsor request failed: ${message}` };
115
109
  }
116
110
  }
117
111
 
@@ -130,16 +124,9 @@ export async function handleMint(
130
124
  calldataService: CalldataService,
131
125
  defaultRecipient: string,
132
126
  userKey: string = "",
133
- tryit: TryitConfig = { tryitKey: "", tryitContract: "", tryitRpc: "", tryitChain: 84532 },
134
127
  ) {
135
- // Determine mode:
136
- // 1. User has private key: mainnet, sign + submit locally
137
- // 2. No key but recipient provided: mainnet, sponsored gas via server
138
- // 3. No key, no recipient: testnet try-it mode
139
- const hasRecipient = !!params.recipient;
140
- const isTestnet = !userKey && !hasRecipient;
141
- const privateKey = userKey || (isTestnet ? tryit.tryitKey : "");
142
128
  // Confirmation path: replay cached intent
129
+ // Check cache first so we can use cached recipient for mode detection
143
130
  if (params.mintId) {
144
131
  const entry = getCachedEntry(params.mintId);
145
132
  if (!entry) {
@@ -154,6 +141,10 @@ export async function handleMint(
154
141
  const cachedPlatform = entry.platform;
155
142
  intentCache.delete(params.mintId);
156
143
 
144
+ // Mode detection using cached recipient
145
+ const hasRecipient = !!cached.recipient;
146
+ const privateKey = userKey;
147
+
157
148
  // Rare Protocol path: mint via SuperRare contracts on Base
158
149
  if (cachedPlatform === "superrare") {
159
150
  if (!privateKey) {
@@ -165,7 +156,7 @@ export async function handleMint(
165
156
  };
166
157
  }
167
158
  try {
168
- const rareResult = await mintOnRareProtocol(cached, privateKey, isTestnet);
159
+ const rareResult = await mintOnRareProtocol(cached, privateKey, false);
169
160
  return {
170
161
  content: [{
171
162
  type: "text" as const,
@@ -183,18 +174,12 @@ export async function handleMint(
183
174
  }
184
175
  }
185
176
 
186
- // Use testnet calldataService if in try-it mode
187
- let activeCalldataService = calldataService;
188
- if (isTestnet) {
189
- activeCalldataService = new CalldataService(tryit.tryitRpc, tryit.tryitContract, tryit.tryitChain);
190
- }
191
- const result = await activeCalldataService.buildMintCalldata(cached);
177
+ const result = await calldataService.buildMintCalldata(cached);
192
178
 
193
179
  // If we have a private key, sign and submit directly
194
180
  if (privateKey) {
195
181
  try {
196
- const provider = isTestnet ? activeCalldataService.provider : calldataService.provider;
197
- const wallet = new ethers.Wallet(privateKey, provider);
182
+ const wallet = new ethers.Wallet(privateKey, calldataService.provider);
198
183
  const tx = await wallet.sendTransaction({
199
184
  to: result.to,
200
185
  data: result.calldata,
@@ -210,12 +195,9 @@ export async function handleMint(
210
195
  tokenType: result.tokenType,
211
196
  soulbound: result.soulbound,
212
197
  recipient: cached.recipient,
213
- chain: isTestnet ? "Base Sepolia (testnet)" : "Base",
214
- explorer: `https://${result.chainId === 8453 ? "" : "sepolia."}basescan.org/tx/${receipt!.hash}`,
198
+ chain: "Base",
199
+ explorer: `https://basescan.org/tx/${receipt!.hash}`,
215
200
  };
216
- if (isTestnet) {
217
- response.note = "This token was minted on Base Sepolia (testnet). To mint on Base mainnet, save a private key to ~/.mint-day/credentials and restart the MCP server.";
218
- }
219
201
  return {
220
202
  content: [{
221
203
  type: "text" as const,
@@ -292,7 +274,6 @@ export async function handleMint(
292
274
  }
293
275
 
294
276
  const recipient = params.recipient || defaultRecipient;
295
-
296
277
  if (!recipient) {
297
278
  return {
298
279
  content: [{
@@ -421,11 +402,7 @@ export async function handleMint(
421
402
  preview.missingFields = intent.missingFields;
422
403
  preview.hint = "Identity tokens (ERC-8004) should include capabilities[], endpoints[], and did. Provide these in metadata for full compliance.";
423
404
  }
424
- if (isTestnet) {
425
- preview.chain = "Base Sepolia (testnet)";
426
- } else {
427
- preview.chain = "Base";
428
- }
405
+ preview.chain = "Base";
429
406
  preview.instruction = "Call mint with mintId to confirm.";
430
407
 
431
408
  return {