Minecraft Creator Tools provides comprehensive tools for packaging add-ons and worlds into distributable formats (.mcpack, .mcaddon, .mcworld).
Packaging Overview
The ProjectExporter class handles all export operations:
- Building projects before export
- Creating .mcpack and .mcaddon files
- Generating .mcworld files with embedded packs
- Deploying to Minecraft folders
- Syncing from GitHub repositories
Reference: app/src/app/ProjectExporter.ts:1
.mcpack
Single behavior or resource packContains: One pack folder
.mcaddon
Complete add-on (BP + RP)Contains: Both packs
.mcworld
World with embedded packsContains: World + packs
Building Before Export
Always prepare projects before packaging:
Project Preparation
import ProjectExporter from "./app/ProjectExporter";
import ProjectBuild from "./app/ProjectBuild";
const projectBuild = await ProjectExporter.prepareProject(project);
if (!projectBuild) {
console.error("Project has errors");
project.errorMessages?.forEach(err => {
console.error(err.message);
});
return;
}
if (projectBuild.isInErrorState) {
console.error("Build failed");
return;
}
What does prepareProject do?
- Runs automated project updates (module versions, etc.)
- Saves the project
- Builds the project (compiles scripts, processes assets)
- Validates the build output
- Returns
ProjectBuild or undefined if errors
Exporting Add-ons
Export as .mcaddon
Create a complete add-on package:
async function exportAddon(project: Project) {
const zipBytes = await ProjectExporter.generateMCAddonAsZip(
creatorTools,
project,
false // returnAsBlob - false for Uint8Array
);
if (!zipBytes) {
console.error("Export failed");
return;
}
// Save to file
const file = storage.rootFolder.ensureFile(`${project.name}.mcaddon`);
file.setContent(zipBytes);
await file.saveContent();
console.log(`Exported to ${project.name}.mcaddon`);
}
Export as Blob (Browser)
For web-based downloads:
async function downloadAddon(project: Project) {
const blob = await ProjectExporter.generateMCAddonAsZip(
creatorTools,
project,
true // returnAsBlob
) as Blob;
if (!blob) return;
// Trigger browser download
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${project.name}.mcaddon`;
a.click();
URL.revokeObjectURL(url);
}
What Gets Packaged
The exporter includes:
Behavior Pack
- manifest.json
- All behavior pack content (entities, items, etc.)
- Compiled scripts (built from TypeScript)
- Excluded: TypeScript source files (*.ts)
Resource Pack
- manifest.json
- All resource pack content (textures, models, etc.)
- Optimized assets
Structure
Uses FolderDeploy.noFolders to create flat structure:addon.mcaddon/
├── pack_name_bp/
│ └── (behavior pack files)
└── pack_name_rp/
└── (resource pack files)
Exporting Worlds
Generate World with Packs
Create a new world with your add-ons embedded:
import { Generator } from "./minecraft/WorldLevelDat";
async function exportWorldWithPacks(project: Project) {
const worldSettings = {
generator: Generator.flat,
betaApisExperiment: true,
gameType: 1, // Creative
difficulty: 0 // Peaceful
};
const mcworld = await ProjectExporter.generateWorldWithPacks(
creatorTools,
project,
worldSettings
);
if (!mcworld) return;
// Get as bytes
const worldBytes = await mcworld.getBytes();
if (!worldBytes) return;
// Save to file
const file = storage.rootFolder.ensureFile(`${project.name}.mcworld`);
file.setContent(worldBytes);
await file.saveContent();
}
Deploy Existing World with Packs
Add your packs to an existing world:
async function exportExistingWorld(
project: Project,
worldProjectItem: ProjectItem
) {
const worldBytes = await ProjectExporter.deployAsWorldAndTestAssets(
creatorTools,
project,
worldProjectItem,
true // returnZipBytes
);
if (!worldBytes) return;
// Save or distribute
const fileName = `${worldProjectItem.name}.mcworld`;
const file = storage.rootFolder.ensureFile(fileName);
file.setContent(worldBytes);
await file.saveContent();
}
World Export Options
Creates a new world with your packs embedded.Use when:
- You want a fresh world for testing
- Creating a template world
- Packaging a sample world for distribution
Result: New world with default terrain + your packs
Exports an existing world from your project.Use when:
- You have a pre-built world in your project
- World has custom terrain/structures
- Preserving existing world data
Result: Copy of existing world + updated packs deployAsWorldAndTestAssets
Exports world with both your packs and Creator Tools assets.Use when:
- Testing with in-game tools
- Need debugging utilities
- Development workflows
Result: World + your packs + Creator Tools pack
Deployment Workflows
Local Minecraft Deployment
Deploy directly to Minecraft’s folder:
async function deployToMinecraft(project: Project) {
const deployTarget = creatorTools.defaultDeploymentTarget;
if (!deployTarget) {
console.error("No deployment target configured");
return;
}
const success = await ProjectExporter.deployProject(
creatorTools,
project,
deployTarget.deployBehaviorPacksFolder!
);
if (success) {
console.log("Deployed to Minecraft!");
}
}
Export to Custom Location
Deploy to any folder:
async function exportToFolder(
project: Project,
targetFolder: IFolder
) {
const projectBuild = await ProjectExporter.prepareProject(project);
if (!projectBuild) return;
await ProjectExporter.deployProjectPacks(
project,
projectBuild,
targetFolder,
undefined,
FolderDeploy.developmentFolders
);
await targetFolder.saveAll();
}
Forbidden Files
Certain files are automatically excluded from packages:
export const ProjectImportExclusions = [
"build", // Build output
"node_modules", // NPM dependencies
"dist", // Distribution folder
"lib", // Library folder
".env", // Environment variables
"*/.*", // Hidden files
"out", // Output folder
"*just.config*", // Build configs
"*package-lock*", // Lock files
"*.mjs*" // Module files
];
Never include sensitive files like .env or credentials in packages!
GitHub Import/Export
Import from GitHub
Sync a project from a GitHub repository:
async function importFromGitHub(
owner: string,
repo: string,
branch: string,
folder?: string
) {
const project = await ProjectExporter.syncProjectFromGitHub(
true, // isNewProject
creatorTools,
repo,
owner,
branch,
folder,
undefined, // projName (auto-generated)
undefined, // project (creates new)
undefined // fileList
);
if (project) {
console.log(`Imported ${project.name} from GitHub`);
}
}
Update from GitHub
Update an existing project from GitHub:
async function updateFromGitHub(project: Project) {
if (!project.originalGitHubOwner || !project.originalGitHubRepoName) {
console.error("Project not linked to GitHub");
return;
}
await ProjectExporter.syncProjectFromGitHub(
false, // isNewProject
creatorTools,
project.originalGitHubRepoName,
project.originalGitHubOwner,
project.originalGitHubBranch,
project.originalGitHubFolder,
project.name,
project,
project.originalFileList,
undefined, // messageUpdater
true // dontOverwriteExistingFiles
);
console.log("Updated from GitHub");
}
Practical Examples
Example 1: Complete Export Workflow
async function completeExport(project: Project) {
console.log("Starting export...");
// 1. Validate project
const infoSet = new ProjectInfoSet(
project,
ProjectInfoSuite.sharing
);
await infoSet.generateForProject();
if (infoSet.errorAndFailCount > 0) {
console.error("Project has errors, cannot export");
return;
}
// 2. Update modules to latest
const runner = new ProjectUpdateRunner(project);
await runner.updateProject(["SCRIPTMODULE"]);
// 3. Export as .mcaddon
const addonBytes = await ProjectExporter.generateMCAddonAsZip(
creatorTools,
project,
false
);
if (!addonBytes) {
console.error("Export failed");
return;
}
// 4. Export test world
const worldBytes = await ProjectExporter.generateFlatBetaApisWorldWithPacksZipBytes(
creatorTools,
project,
`${project.name} Test World`
);
// 5. Save files
const addonFile = storage.rootFolder.ensureFile(`${project.name}.mcaddon`);
addonFile.setContent(addonBytes);
await addonFile.saveContent();
if (worldBytes) {
const worldFile = storage.rootFolder.ensureFile(`${project.name}_test.mcworld`);
worldFile.setContent(worldBytes);
await worldFile.saveContent();
}
console.log("Export complete!");
}
async function exportAllFormats(project: Project) {
const outputFolder = storage.rootFolder.ensureFolder("exports");
await outputFolder.ensureExists();
// Export addon
const addonBytes = await ProjectExporter.generateMCAddonAsZip(
creatorTools,
project,
false
);
if (addonBytes) {
const addonFile = outputFolder.ensureFile(`${project.name}.mcaddon`);
addonFile.setContent(addonBytes);
await addonFile.saveContent();
}
// Export test world
const worldSettings = {
generator: Generator.flat,
betaApisExperiment: true
};
const mcworld = await ProjectExporter.generateWorldWithPacks(
creatorTools,
project,
worldSettings
);
if (mcworld) {
const worldBytes = await mcworld.getBytes();
if (worldBytes) {
const worldFile = outputFolder.ensureFile(`${project.name}.mcworld`);
worldFile.setContent(worldBytes);
await worldFile.saveContent();
}
}
// Export to folder (unzipped)
const folderExport = outputFolder.ensureFolder(project.name);
await folderExport.ensureExists();
const projectBuild = await ProjectExporter.prepareProject(project);
if (projectBuild) {
await ProjectExporter.deployProjectPacks(
project,
projectBuild,
folderExport,
undefined,
FolderDeploy.noFolders
);
await folderExport.saveAll();
}
console.log(`Exported to ${outputFolder.fullPath}`);
}
Example 3: Batch Export Multiple Projects
async function batchExport(projects: Project[]) {
const results = [];
for (const project of projects) {
try {
console.log(`Exporting ${project.name}...`);
const bytes = await ProjectExporter.generateMCAddonAsZip(
creatorTools,
project,
false
);
if (bytes) {
const file = storage.rootFolder.ensureFile(
`${project.name}.mcaddon`
);
file.setContent(bytes);
await file.saveContent();
results.push({
name: project.name,
success: true,
size: bytes.length
});
} else {
results.push({
name: project.name,
success: false,
error: "Export returned no bytes"
});
}
} catch (error) {
results.push({
name: project.name,
success: false,
error: error.message
});
}
}
// Print summary
console.log("\nExport Summary:");
for (const result of results) {
if (result.success) {
console.log(`✓ ${result.name} (${result.size} bytes)`);
} else {
console.log(`✗ ${result.name}: ${result.error}`);
}
}
}
Optimizing Exports
Minimize Package Size
Remove development files
Ensure ProjectImportExclusions are applied (automatic)
Optimize textures
Compress textures before export
Strip comments
Remove comments from JSON files (if desired)
Validate content
Remove unused assets flagged by validation
Pre-export Checklist
Best Practices
Always validate first
Run validation before exporting to catch issues early.
Update dependencies
Keep script modules and versions current before export.
Test exports
Import and test exported packages before distribution.
Version your exports
Include version numbers in export filenames.
Troubleshooting
Export is empty or missing files
Check:
- Project build completed successfully
- Files are saved before export
- Storage folder is loaded (
await folder.load())
- No file exclusion rules blocking content
Scripts not working in exported pack
Verify:
- Scripts were compiled (check projectBuild)
- Script modules in manifest.json are correct
- TypeScript files are excluded (they should be)
- JavaScript files are included in scripts/
World doesn't load exported packs
Ensure:
- Pack references added to world manifests
- Pack UUIDs match between manifest and world
- Experiments are enabled if needed
- Packs are in correct world folders
Optimize:
- Use
FolderDeploy.noFolders when possible
- Exclude large unnecessary files
- Clear project cache before export
- Process files in batches for large projects