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 +2 -12
- package/dist/tools/mint.d.ts +1 -8
- package/dist/tools/mint.js +14 -31
- package/package.json +1 -1
- package/src/index.ts +2 -12
- package/src/tools/mint.ts +14 -37
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
|
|
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() {
|
package/dist/tools/mint.d.ts
CHANGED
|
@@ -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
|
|
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 {};
|
package/dist/tools/mint.js
CHANGED
|
@@ -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
|
-
|
|
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 = ""
|
|
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,
|
|
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
|
-
|
|
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
|
|
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:
|
|
182
|
-
explorer: `https
|
|
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
|
-
|
|
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
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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:
|
|
214
|
-
explorer: `https
|
|
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
|
-
|
|
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 {
|