ForTem MCP Documentation
ForTem provides two ways for game developers to manage NFT items on the SUI blockchain:
an MCP Server that AI coding assistants (Claude Code, Cursor, VS Code Copilot) can call directly,
and a JavaScript SDK (@fortemlabs/sdk-js) for server-side operations.
Use them individually or together.
What is MCP?
The Model Context Protocol (MCP) is an open standard that lets AI coding assistants call external APIs as "tools." Instead of writing API integration code yourself, you describe what you want in natural language and the AI assistant calls ForTem's MCP server directly -- creating collections, minting items, querying ownership, and more.
Why use ForTem MCP?
- Zero boilerplate -- ask your AI assistant to "create a collection" and it handles the API call
- Faster iteration -- manage NFT items without leaving your editor
- Works alongside SDK -- use MCP for management tasks, SDK for production server-side logic
Quick Start
Prerequisites
- A ForTem developer account and API key (from the ForTem developer dashboard)
- An AI coding assistant that supports MCP (Claude Code, Cursor, VS Code Copilot, etc.)
MCP Server URLs
| Environment | URL |
|---|---|
| Production | https://mcp.fortem.gg/mcp |
| Development | https://mcp-dev.fortem.gg/mcp |
Connect MCP Server
MCP authenticates via OAuth in the browser — no API key needed for MCP setup. Just add the server URL.
// .mcp.json in your project root
{
"mcpServers": {
"fortem": {
"type": "http",
"url": "https://mcp.fortem.gg/mcp"
}
}
}
// .cursor/mcp.json in your project root
{
"mcpServers": {
"fortem": {
"type": "http",
"url": "https://mcp.fortem.gg/mcp"
}
}
}
// .vscode/mcp.json
{
"servers": {
"fortem": {
"type": "http",
"url": "https://mcp.fortem.gg/mcp"
}
}
}
SDK Installation
npm install @fortemlabs/sdk-js
When to Use MCP vs SDK vs Both
| Task | MCP | SDK | Recommendation |
|---|---|---|---|
| Create collection | Yes | Yes | Both work, MCP is simpler |
| List collections | Yes | Yes | MCP is faster for quick lookups |
| Mint item (no image) | Yes | Yes | MCP is simpler |
| Mint item (with image) | No | Yes | MCP has base64 size limit — Why? |
| Upload image | No | Yes | MCP cannot handle large base64 payloads — Why? |
| Query items | Yes | Yes | MCP is faster for quick lookups |
| Verify user | Yes | Yes | MCP is simpler |
| Server-side verification | No | Yes | Serverless functions need SDK |
MCP Tools Reference
These tools are automatically available to your AI assistant once the MCP server is configured.
SDK Reference
Authentication
import { createFortemClient } from '@fortemlabs/sdk-js';
const fortem = createFortemClient({
apiKey: 'YOUR_API_KEY',
network: 'mainnet'
});
// Get nonce and authenticate
const { data: { nonce } } = await fortem.auth.getNonce();
await fortem.auth.getAccessToken(nonce);
Image Upload
const file = new File([buffer], 'image.png', { type: 'image/png' });
const { data: { itemImage } } = await fortem.items.uploadImage(collectionId, file);
Mint Item
await fortem.items.create(collectionId, {
name: 'Flame Sword',
description: 'A legendary weapon forged in fire',
quantity: 1,
redeemCode: 'a1b2-c3d4-e5f6-7890',
recipientAddress: '0x...',
itemImage: itemImage, // IPFS hash from uploadImage
attributes: [
{ trait_type: 'Element', value: 'Fire' },
{ trait_type: 'Rarity', value: 'Legendary' }
]
});
Get Item
const { data: item } = await fortem.items.get(collectionId, redeemCode);
AI Prompt Templates
Copy these prompts into your AI coding assistant to perform common ForTem operations.
Creating a Collection
Use the fortem MCP to create an NFT collection.
- Name: [name]
- Description: [description]
Return the collection ID and objectId.
Minting Items with Images
Use the fortem SDK to upload images and mint items.
MCP has base64 size limits, so image upload MUST use the SDK.
1. Install @fortemlabs/sdk-js if not installed
2. Upload these images to fortem and get IPFS hashes:
- [image paths]
3. Mint items with the uploaded hashes:
- Collection ID: [ID]
- Recipient wallet: [address]
- Redeem code: generate random xxxx-xxxx-xxxx-xxxx format
- Quantity: [N]
SDK auth:
const fortem = createFortemClient({ apiKey: '[KEY]', network: 'mainnet' });
const { data: { nonce } } = await fortem.auth.getNonce();
await fortem.auth.getAccessToken(nonce);
Image upload:
const file = new File([buffer], 'filename.png', { type: 'image/png' });
const { data: { itemImage } } = await fortem.items.uploadImage(collectionId, file);
Create a Node.js script to run this. Read API key from .env.local.
Redeem Code Verification (Server-side)
Create a serverless function to verify fortem redeem codes.
Flow:
1. Client sends POST { redeemCode, walletAddress }
2. Server uses fortem SDK: items.get(collectionId, redeemCode)
3. Verify: item exists, status is 'REDEEMED', owner matches wallet
4. Use KV store for duplicate prevention
5. Return card info on success
Security: API key server-side only, wallet ownership check, server-side dedup.
Image Constraints
| Property | Requirement |
|---|---|
| Max file size | 200KB |
| Dimensions | 256 x 256 pixels |
| Formats | JPEG, PNG, WebP |
| Upload method | SDK only (MCP has base64 size limit) |
sharp or canvas to resize images before uploading.
import sharp from 'sharp';
const resized = await sharp('input.png')
.resize(256, 256, { fit: 'cover' })
.png({ quality: 80 })
.toBuffer();
// Verify size
if (resized.length > 200 * 1024) {
throw new Error('Image exceeds 200KB limit');
}
Redeem Code Format
| Property | Specification |
|---|---|
| Format | xxxx-xxxx-xxxx-xxxx (16 hex characters with dashes) |
| Generation | Must use crypto.randomBytes (cryptographically secure) |
| Uniqueness | Must be unique per item |
| Predictability | Must never be predictable or sequential |
import crypto from 'crypto';
function generateRedeemCode() {
const bytes = crypto.randomBytes(8);
const hex = bytes.toString('hex');
return `${hex.slice(0,4)}-${hex.slice(4,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}`;
}
// Example output: "a3f1-b2c4-d5e6-7890"
Complete Example: Game Item Minting Pipeline
A full Node.js script that authenticates, resizes an image, uploads it, and mints an NFT item.
import { createFortemClient } from '@fortemlabs/sdk-js';
import sharp from 'sharp';
import crypto from 'crypto';
import fs from 'fs';
import { config } from 'dotenv';
config({ path: '.env.local' });
// ── Config ──────────────────────────────────────
const API_KEY = process.env.FORTEM_API_KEY;
const COLLECTION_ID = process.env.FORTEM_COLLECTION_ID;
const RECIPIENT = process.env.RECIPIENT_WALLET;
const IMAGE_PATH = './assets/sword.png';
// ── Generate redeem code ────────────────────────
function generateRedeemCode() {
const hex = crypto.randomBytes(8).toString('hex');
return `${hex.slice(0,4)}-${hex.slice(4,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}`;
}
async function main() {
// 1. Authenticate
const fortem = createFortemClient({
apiKey: API_KEY,
network: 'mainnet'
});
const { data: { nonce } } = await fortem.auth.getNonce();
await fortem.auth.getAccessToken(nonce);
console.log('Authenticated successfully');
// 2. Resize image to 256x256, ensure under 200KB
const resized = await sharp(IMAGE_PATH)
.resize(256, 256, { fit: 'cover' })
.png({ quality: 80 })
.toBuffer();
if (resized.length > 200 * 1024) {
throw new Error(`Image too large: ${(resized.length / 1024).toFixed(1)}KB`);
}
console.log(`Image resized: ${(resized.length / 1024).toFixed(1)}KB`);
// 3. Upload image to ForTem (returns IPFS hash)
const file = new File([resized], 'sword.png', { type: 'image/png' });
const { data: { itemImage } } = await fortem.items.uploadImage(
COLLECTION_ID, file
);
console.log(`Uploaded image: ${itemImage}`);
// 4. Mint item with all attributes
const redeemCode = generateRedeemCode();
await fortem.items.create(COLLECTION_ID, {
name: 'Flame Sword',
description: 'A legendary weapon forged in the heart of a volcano',
quantity: 1,
redeemCode: redeemCode,
recipientAddress: RECIPIENT,
itemImage: itemImage,
attributes: [
{ trait_type: 'Element', value: 'Fire' },
{ trait_type: 'Rarity', value: 'Legendary' },
{ trait_type: 'Attack', value: '95' }
]
});
// 5. Log the redeem code
console.log(`Minted successfully!`);
console.log(`Redeem code: ${redeemCode}`);
console.log(`Recipient: ${RECIPIENT}`);
}
main().catch(console.error);
.env.local):FORTEM_API_KEY=your_api_key_hereFORTEM_COLLECTION_ID=your_collection_idRECIPIENT_WALLET=0x...
FAQ
Why can't I upload images or mint items with images directly through MCP?
The ForTem MCP server is hosted on AWS (cloud), not on your local machine.
This means it cannot access files on your local filesystem.
For example, if your image is at /Users/name/image.png,
the MCP server running on AWS has no way to read that file.
Additionally, LLMs have inherent limitations when generating images as base64 data, making it impractical to pass image data through the MCP protocol reliably.
Workaround: Use SDK for Image Upload + MCP for Everything Else
When using an AI coding assistant (Claude, Cursor, etc.) with the ForTem MCP connected, simply instruct it to use the SDK for image uploads and MCP for the rest. The AI assistant can read local files, call the SDK, and use MCP tools — all in one workflow.
Use my ForTem API key from .env to upload images via the SDK (cURL or @fortemlabs/sdk-js),
then mint the items using the ForTem MCP.
1. Upload image from ./assets/sword.png using the SDK
2. Use the returned IPFS hash to mint the item via MCP:
- Collection ID: [ID]
- Name: Legendary Sword
- Recipient: [wallet address]
- Redeem code: generate random xxxx-xxxx-xxxx-xxxx format
The AI assistant will automatically handle the SDK authentication, file reading, image upload, and MCP minting — all from a single prompt.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| MCP auth failure | Session expired or invalid API key | Reload your IDE or re-save .mcp.json |
| Image upload fails via MCP | base64 payload exceeds size limit | Use SDK items.uploadImage() instead |
| 413 Entity Too Large | Image exceeds 200KB | Compress/resize with sharp before upload |
| Default image shown after mint | Wrong itemImage hash |
Use the exact hash returned from uploadImage response |
| redeemCode collision | Duplicate code generated | Use crypto.randomBytes and check uniqueness |
| SDK token expired | Access token has 5-minute TTL | SDK auto-refreshes; re-authenticate if issues persist |
| recipientAddress error | Missing or empty wallet address | Provide a valid SUI wallet address (starts with 0x) |