Skip to main content
Glama

create_asset

Create NFT assets by uploading files or base64 data to specified collections, supporting both ERC721 and ERC1155 standards with customizable metadata.

Instructions

Create a new NFT asset from a local file or base64 data

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filePathNoAbsolute path to the media file (for local files)
fileDataNoBase64 encoded file data (for Claude Desktop)
fileNameNoOriginal filename (required when using fileData)
mimeTypeNoMIME type of the file (required when using fileData, e.g., 'image/png')
contractIdYesID of the collection to mint the asset in
titleYesAsset title (3-120 characters)
descriptionNoAsset description (optional, max 255 characters)
locationNoLocation where the asset was created (optional, max 100 characters)
editionsNoNumber of editions (for ERC1155 collections only, 1-1000)
shareWithCommunityNoMake the asset discoverable by the community (optional, default: false)

Implementation Reference

  • The core handler function that executes the create_asset tool: parses input, handles file input from path or base64, performs upload using UploadPrimitives, and initiates NFT minting via api.assets.startMinting.
    export async function createAsset(params: CreateAssetInput): Promise<CreateAssetResult> {
      try {
        // Validate input using our schema
        const validatedParams = createAssetInputSchema.parse(params);
    
        // Determine how to create the file - either from disk or base64 data
        let file: NodeFileData;
    
        if (validatedParams.fileData && validatedParams.fileName && validatedParams.mimeType) {
          // Option 1: Create file from base64 data (for Claude Desktop)
          file = createNodeFileFromData(validatedParams.fileData, validatedParams.fileName, validatedParams.mimeType);
        } else if (validatedParams.filePath) {
          // Option 2: Create file from disk path
          if (!fs.existsSync(validatedParams.filePath)) {
            return {
              success: false,
              error: `File not found: ${validatedParams.filePath}`,
            };
          }
          file = createNodeFileFromPath(validatedParams.filePath);
        } else {
          return {
            success: false,
            error: "Either filePath or (fileData + fileName + mimeType) must be provided",
          };
        }
    
        const fileType = detectFileTypeFromMimeType(file.type);
    
        if (fileType === FileType.Unknown) {
          return {
            success: false,
            error: `Unsupported file type: ${file.type}`,
          };
        }
    
        // Create upload primitives instance
        const uploader = new UploadPrimitives({
          disableThumbnail: false,
          isPrivate: false,
        });
    
        // Stage 1: Prepare upload
        const fileMetadata = JSON.stringify({
          lastModified: file.lastModified,
          size: file.size,
          type: file.type,
          name: file.name,
        });
    
        await uploader.prepareUpload({
          file,
          metadata: fileMetadata,
          fileType,
          source: FileSource.Upload,
          deviceId: MCP_CONFIG.DEVICE_ID,
        });
    
        if (!uploader.fileId) {
          throw new Error("Failed to prepare file upload");
        }
    
        // Stage 2: Upload all chunks
        await uploader.uploadAllChunks();
    
        // Stage 3: Finalize upload
        await uploader.finalizeUpload();
    
        // Stage 4: Start minting
        const mintResponse = await api.assets.startMinting({
          fileId: uploader.fileId,
          editions: validatedParams.editions,
          contractId: validatedParams.contractId,
          shareWithCommunity: validatedParams.shareWithCommunity ?? false,
          isEncrypted: false, // Not supporting encryption in MCP for now
          metadata: {
            attributes: [
              {
                key: "title",
                value: validatedParams.title,
                type: Metadata_AttributeType.STRING,
              },
              ...(validatedParams.description
                ? [
                    {
                      key: "description",
                      value: validatedParams.description,
                      type: Metadata_AttributeType.STRING,
                    },
                  ]
                : []),
              ...(validatedParams.location
                ? [
                    {
                      key: "location",
                      value: validatedParams.location,
                      type: Metadata_AttributeType.STRING,
                    },
                  ]
                : []),
              {
                key: "appName",
                value: "Uranium MCP",
                type: Metadata_AttributeType.STRING,
              },
              {
                key: "appVersion",
                value: "1.0.0",
                type: Metadata_AttributeType.STRING,
              },
            ],
          },
        });
    
        if (mintResponse.status !== "ok") {
          return {
            success: false,
            error: mintResponse.errorCode || "Failed to start minting",
          };
        }
    
        return {
          success: true,
          data: {
            asset: {
              fileId: uploader.fileId,
              contractId: validatedParams.contractId,
              title: validatedParams.title,
              description: validatedParams.description,
              location: validatedParams.location,
              editions: validatedParams.editions,
              shareWithCommunity: validatedParams.shareWithCommunity,
            },
            message: `Asset "${validatedParams.title}" created successfully and is being minted!`,
            progress: {
              stage: "Minting NFT...",
              percentage: 100,
            },
          },
        };
      } catch (error) {
        if (error instanceof z.ZodError) {
          const errorMessages = error.issues.map((err) => err.message).join(", ");
          return {
            success: false,
            error: `Validation error: ${errorMessages}`,
          };
        }
    
        return {
          success: false,
          error: error instanceof Error ? error.message : "Unknown error occurred",
        };
      }
    }
  • Zod schema defining the input parameters for createAsset, including validation for title, contractId, file inputs (path or base64), etc.
    export const createAssetSchema = z
      .object({
        title: z
          .string()
          .min(3, "Title must be at least 3 characters long")
          .max(120, "Title must be no more than 120 characters long"),
        description: z.string().max(255, "Description must be no more than 255 characters long").optional(),
        location: z.string().max(100, "Location must be no more than 100 characters long").optional(),
        contractId: z.string().min(1, "Please select a collection"),
        editions: z
          .number()
          .min(1, "Editions must be at least 1")
          .max(1000, "Editions must be no more than 1000")
          .optional(),
        shareWithCommunity: z.boolean().optional().default(false),
    
        // Option 1: File from disk
        filePath: z.string().optional(),
    
        // Option 2: Base64 data (for Claude Desktop)
        fileData: z.string().optional(), // base64 encoded data
        fileName: z.string().optional(), // original filename
        mimeType: z.string().optional(), // MIME type
      })
      .refine((data) => data.filePath || (data.fileData && data.fileName && data.mimeType), {
        message: "Either filePath OR (fileData + fileName + mimeType) must be provided",
        path: ["filePath"], // This will show the error on the filePath field
      });
  • src/server.ts:77-89 (registration)
    MCP server request handler registration for the 'create_asset' tool, which validates arguments and calls the createAsset handler.
    case "create_asset": {
      // Validate and parse arguments
      const validatedArgs = createAssetInputSchema.parse(args);
      const result = await createAsset(validatedArgs);
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(result, null, 2),
          },
        ],
      };
    }
  • Re-exports the input schema and derives TypeScript type for createAsset.
    export const createAssetInputSchema = createAssetSchema;
    export type CreateAssetInput = z.infer<typeof createAssetInputSchema>;
  • TypeScript interface defining the return type of createAsset.
    export interface CreateAssetResult {
      success: boolean;
      data?: {
        asset: {
          fileId: string;
          contractId: string;
          title: string;
          description?: string;
          location?: string;
          editions?: number;
          shareWithCommunity?: boolean;
        };
        message: string;
        progress?: {
          stage: string;
          percentage?: number;
        };
      };
      error?: string;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. While 'Create a new NFT asset' implies a write/mutation operation, the description doesn't address critical behavioral aspects like authentication requirements, rate limits, side effects (e.g., minting on-chain), error handling, or what happens upon success (e.g., returns an asset ID). For a creation tool with zero annotation coverage, this is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's purpose without unnecessary words. It's front-loaded with the core action and resource, making it easy to understand at a glance. Every part of the sentence earns its place by specifying the creation target and input methods.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a 10-parameter creation tool with no annotations and no output schema, the description is insufficiently complete. It doesn't explain what the tool returns (e.g., asset ID, transaction hash), behavioral traits like permissions or costs, or how it interacts with the blockchain. For a tool that likely involves minting and on-chain operations, more context is needed to guide effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, meaning all parameters are documented in the input schema. The description adds minimal value beyond the schema by mentioning the two input sources (local file or base64 data), which loosely maps to filePath and fileData parameters. However, it doesn't provide additional context, constraints, or examples beyond what's already in the schema descriptions, so the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Create a new NFT asset') and the resource involved ('from a local file or base64 data'), distinguishing it from sibling tools like create_collection (which creates collections) and list_assets/list_collections (which are read operations). It precisely communicates what the tool does without being vague or tautological.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like create_collection or list_assets. It mentions the input sources (local file or base64 data) but doesn't specify prerequisites, dependencies, or contextual scenarios for choosing this tool over others. There's no explicit when/when-not or alternative tool references.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/xkelxmc/uranium-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server