Starknet NFT Minting Guide for MCP
Overview
This document describes the complete process of minting NFTs on the Starknet blockchain using MCP (Model Context Protocol) tools. The process involves several steps that must be executed in a specific order to successfully create and mint NFTs.
📋 Prerequisites: Before using these tools, make sure you have set up MCP integration with your API key.
Account deployment note: Starknet wallet addresses are account contracts. The
creator_addressand eachitems[].receivermust be deployed on the target network, otherwise you may see errors likeRequested contract address ... is not deployed.
The same operations are available as REST API endpoints (see starknet.hono.ts): POST /deploy, POST /complete-deploy, POST /mint, POST /complete-mint. REST routes require auth and accept the same JSON bodies as the data payloads described below.
All four operations are now network-aware. Pass the same network value through the full lifecycle (deploy -> complete-deploy -> mint -> complete-mint). Supported values are "SN_MAIN" and "SN_SEPOLIA", and the connected wallet must be on the same network.
Process Flow
sequenceDiagram participant MCP as MCP Client participant Tools as MCP Tools participant API as Backend API participant Frontend as Frontend participant Blockchain as Starknet
Note over MCP,Blockchain: Deploy Collection
MCP->>Tools: starknet-deploy-collection
Tools-->>MCP: calls[]
MCP->>Frontend: pass calls[]
Frontend->>Blockchain: account.execute(calls)
Blockchain-->>Frontend: receipt (transaction_hash)
Note over MCP,Blockchain: Complete Deployment
MCP->>Tools: starknet-complete-deploy
Tools-->>MCP: collection data
Note over MCP,Blockchain: Approve Token Before Mint
Frontend->>Blockchain: approve(collection_address, total_amount)
Blockchain-->>Frontend: approve receipt
Note over MCP,Blockchain: Mint NFT
MCP->>Tools: starknet-mint-nft
Tools-->>MCP: calls[]
MCP->>Frontend: pass calls[]
Frontend->>Blockchain: account.execute(calls)
Blockchain-->>Frontend: receipt (transaction_hash)
Note over MCP,Blockchain: Complete Mint
MCP->>Tools: starknet-complete-mint
Tools-->>MCP: NFT records
MCP Tools
All tools accept input under a data key: pass { data: { ...fields } } when calling a tool. The schemas below describe the contents of data.
1. Deploy Collection
Tool: starknet-deploy-collection
Description: Deploy a collection. As a result of executing this command, you will receive a transaction that needs to be signed and sent to the blockchain.
Input data:
{
name: string, // Collection name (required, min 1 char, cannot be empty/whitespace)
symbol: string, // Collection symbol (required, min 1 char, cannot contain spaces)
description?: string, // Optional description
feeNumerator: number, // Royalty numerator in percentage (0-8, e.g., 8 = 8%)
transferable: boolean, // Whether NFT can be transferred
maxTotalSupply: number, // Maximum NFT count
mintPrice: number, // Base price per mint (human amount, e.g., 2, 0.5)
whitelistMintPrice: number, // Discounted price for whitelisted minters (human amount)
paymentToken: "STRK"|"ETH"|"USDC", // Payment token
creator: string, // Creator wallet address (Starknet address format)
externalUrl?: string | null, // Optional external website/app link
referralCode?: string, // Optional referral code
network: "SN_MAIN"|"SN_SEPOLIA" // Target Starknet network
}
Note: The backend automatically calculates total royalty (platform fee + creator fee) and validates it doesn't exceed 80%.
Output data:
{
calls: [
{
contractAddress: string, // Factory contract address
entrypoint: "produce", // Entrypoint name
calldata: any[] // Compiled calldata
}
]
}
Note: Returns an object { calls: [...] } (or { error: string } on failure). After executing the transaction, use the transaction hash with starknet-complete-deploy and payload under data to finalize collection creation in the database.
2. Complete Deployment
Tool: starknet-complete-deploy
Description: Validates the deployment transaction receipt and creates or refreshes the collection record in the database. When activityId or hubId is provided, the linked entity is published with passStrategy: STATIC to enable QR pass flow.
Input: Pass as { data: { ... } }:
{
hash: string, // Transaction hash from deploy step (required, non-zero)
gasless?: boolean, // Enable gasless transactions (optional)
hubId?: string | null, // Optional hub ID for whitelisting
activityId?: string | null, // Optional activity ID
whitelist_hubIDs?: string[], // Array of hub IDs for whitelist (optional)
whitelist_userIDs?: string[], // Array of user IDs for whitelist (optional)
attributes?: [ // Default attributes for NFTs (optional)
{
trait_type: string,
display_type?: "string" | "number" | "date" | "boost_number" | "boost_percentage", // Optional, defaults to "string"
value: string
}
],
network: "SN_MAIN"|"SN_SEPOLIA" // Same network used during deploy
}
Output data:
{
// NftCollection object with all collection data
id: string,
contractAddress: string,
// ... other collection fields
}
3. Approve Payment Token (Required before Mint)
⚠️ Important: Before minting NFTs, you must approve the collection contract to spend the required amount of payment tokens on your behalf.
Important units: ERC20 approve(amount) expects the base-units amount (u256), not a human decimal string.
If your items[].price values are human amounts, convert them to base units using token decimals before summing and approving.
Token addresses depend on the selected Starknet network:
const STARKNET_TOKEN_ADDRESSES = {
SN_MAIN: {
STRK: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
ETH: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
USDC: "0x033068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb",
},
SN_SEPOLIA: {
STRK: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
ETH: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
USDC: "0x0512feac6339ff7889822cb5aa2a86c848e9d392bb0e3e237c008674feed8343",
},
};
Implementation:
// Example: approve 1 token (18 decimals) => 1e18 base units
const approveCall = {
contractAddress: STARKNET_TOKEN_ADDRESSES[network].STRK,
entrypoint: "approve",
calldata: [
collection_address, // spender
"1000000000000000000", // amount
"0", // high part
],
};
await account.execute([approveCall]);
4. Mint NFT
Tool: starknet-mint-nft
Description: Mint an NFT. As a result of executing this command, you will receive a transaction that needs to be signed and sent to the blockchain.
Input data:
{
collection_address: string, // Created collection address
items: [
{
receiver: string, // NFT receiver address
name: string, // NFT name
description: string, // NFT description
price: string, // Human amount string (e.g. "0.2") - backend converts to u256 base-units by token decimals
quantity: number, // Number of copies
image?: string, // NFT image URL
externalUrl?: string, // External link
animation_url?: string, // Animation URL
attributes?: [ // NFT attributes
{
trait_type: string,
value: string
}
]
}
],
network: "SN_MAIN"|"SN_SEPOLIA" // Same network used during deploy
}
Output data:
{
calls: [
{
contractAddress: string,
entrypoint: string,
calldata: any[]
}
]
}
Note: Returns an object { calls: [...] }. After executing the transaction, use the transaction hash with starknet-complete-mint MCP tool to parse the receipt and create NFT records in the database.
5. Complete Mint
Tool: starknet-complete-mint
Description: Parses the mint transaction receipt, extracts minted NFT information (token IDs, receivers), fetches metadata from tokenUri, calculates minted_price, and creates NFT records in the database. It also returns the minter Starknet wallet in cryptoAddress response field without persisting Starknet wallet addresses in crypto_addresses. Requires MCP auth (API key / user context).
Input: Pass as { data: { hash, network } }:
{
hash: string, // Transaction hash from mint step (required)
network: "SN_MAIN"|"SN_SEPOLIA" // Same network used during mint
}
Output data:
[
{
id: string,
name: string,
hash: string,
address: string,
block_number: string | null,
token_id: string,
token_uri: string | null,
image: string | null,
userId: string,
minted_price: number | null,
attributes: [
{
trait_type: string,
display_type?: "string" | "number" | "date" | "boost_number" | "boost_percentage",
value: string
}
] | null,
cryptoAddress: {
address: string
},
receiver: string | null,
serialNumber?: string
}
]
Note:
- Automatically parses
Transferevents from the transaction receipt to identify minted NFTs and the actual NFT contract - Fetches and parses metadata from
tokenUri(supports JSON strings, HTTP/HTTPS URLs, and Data URIs) - Creates NFT records in DB during
complete-mint; repeat calls for the same transaction are not intended as the primary flow complete-deployandcomplete-mintmust be called with the samenetworkvalue that was used for the original transaction- Extracts metadata fields:
name,image,description,external_url,animation_url, andattributes - If the collection is linked to an
activityIdorhubId, QR passes are created during NFT persistence and QR emails use Voyager links for Starknet
Usage
// 1. Deploy collection using MCP tool
const collectionData = {
name: "My NFT Collection",
symbol: "MNFT",
description: "A unique collection",
feeNumerator: 5, // 5% royalty
transferable: true,
maxTotalSupply: 10000,
mintPrice: 2,
whitelistMintPrice: 1,
paymentToken: "USDC",
creator: "0x...", // Your Starknet wallet address
externalUrl: "https://example.com",
referralCode: "0", // Optional
};
const deployResult = await mcp.callTool("starknet-deploy-collection", {
data: collectionData,
});
const calls = JSON.parse(deployResult.content[0].text); // Extract calls array from MCP response
// On frontend: Execute transaction
const receipt = await account.execute(calls);
const transactionHash = receipt.transaction_hash;
// 2. Complete deployment (create collection in database)
// After transaction is confirmed, call complete-deploy MCP tool with payload under data
const completeResult = await mcp.callTool("starknet-complete-deploy", {
data: {
hash: transactionHash,
gasless: false, // Optional
hubId: null, // Optional
activityId: null, // Optional
whitelist_hubIDs: [], // Optional
whitelist_userIDs: [], // Optional
attributes: [
// Optional
{
trait_type: "Color",
display_type: "string",
value: "Blue",
},
],
},
});
const collection = JSON.parse(completeResult.content[0].text); // Extract collection data from MCP response
const collectionAddress = collection.contractAddress; // Get collection address from response
// 3. Before minting: Approve payment token
import { parseUnits } from "viem";
const tokenDecimals = 18; // STRK/ETH are 18; USDC is 6 (see token address section below)
const totalAmount = mintData.items
.reduce((sum, item) => {
const itemBaseUnits = parseUnits(item.price, tokenDecimals);
return sum + itemBaseUnits * BigInt(item.quantity);
}, 0n)
.toString();
const tokenAddress = {
STRK: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
ETH: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
// USDC addresses differ by network. Prefer the addresses used by the backend configuration:
// - Sepolia (native USDC): 0x0512feAc6339Ff7889822cb5aA2a86C848e9D392bB0E3E237C008674feeD8343
// - Mainnet (native USDC): 0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb
USDC: "0x0512feAc6339Ff7889822cb5aA2a86C848e9D392bB0E3E237C008674feeD8343",
}[paymentToken]; // Use the same payment token as in collection
const approveCall = {
contractAddress: tokenAddress,
entrypoint: "approve",
calldata: [collection_address, totalAmount, "0"],
};
// Execute approve transaction
await account.execute([approveCall]);
// 4. Mint NFT using MCP tool
const mintData = {
collection_address: collectionAddress, // From complete-deploy response
items: [
{
receiver: "0x...", // Receiver wallet address
name: "NFT One",
description: "Description of the NFT",
price: "0.2", // Human amount string
quantity: 1,
image: "https://example.com/image.png", // Optional
externalUrl: "https://example.com/external-link.html", // Optional
animation_url: "https://example.com/animation.mp4", // Optional
attributes: [
// Optional
{
trait_type: "Rarity",
value: "Common",
},
],
},
],
};
const mintResult = await mcp.callTool("starknet-mint-nft", {
data: mintData,
});
const { calls } = JSON.parse(mintResult.content[0].text); // Extract calls from MCP response
// On frontend: Execute mint transaction
const mintReceipt = await account.execute(calls);
const mintTransactionHash = mintReceipt.transaction_hash;
// 5. Complete mint (parse receipt and create NFT records in database)
// After transaction is confirmed, call complete-mint MCP tool
const completeMintResult = await mcp.callTool("starknet-complete-mint", {
data: {
hash: mintTransactionHash,
},
});
const nfts = JSON.parse(completeMintResult.content[0].text); // Extract NFT records from MCP response
console.log(`Successfully created ${nfts.length} NFT records`);
Errors
MCP tools return errors in the standard MCP error format when operations fail.