maintainx-migration-deep-dive
Execute complete platform migrations to or from MaintainX. Use when migrating from legacy CMMS systems, performing major re-platforming, or transitioning to MaintainX from spreadsheets or other tools. Trigger with phrases like "migrate to maintainx", "maintainx migration", "cmms migration", "switch to maintainx", "maintainx data migration".
Install
mkdir -p .claude/skills/maintainx-migration-deep-dive && curl -L -o skill.zip "https://mcp.directory/api/skills/download/6861" && unzip -o skill.zip -d .claude/skills/maintainx-migration-deep-dive && rm skill.zipInstalls to .claude/skills/maintainx-migration-deep-dive
About this skill
MaintainX Migration Deep Dive
Current State
!node --version 2>/dev/null || echo 'N/A'
Overview
Comprehensive guide for migrating to MaintainX from legacy CMMS systems (Maximo, UpKeep, Fiix), spreadsheets, or custom databases.
Prerequisites
- MaintainX account with API access
- Access to source system data (CSV export, API, or database)
- Node.js 18+
Migration Phases
Phase 1: Assess → Phase 2: Map → Phase 3: Migrate → Phase 4: Validate
(Audit source) (Schema mapping) (ETL + import) (Verify + cutover)
Instructions
Step 1: Source System Assessment
// scripts/assess-source.ts
import { parse } from 'csv-parse/sync';
import { readFileSync } from 'fs';
interface AssessmentReport {
totalRecords: number;
recordTypes: Record<string, number>;
dataQuality: {
missingFields: Record<string, number>;
duplicates: number;
invalidDates: number;
};
}
function assessCSV(filePath: string, columns: string[]): AssessmentReport {
const content = readFileSync(filePath, 'utf-8');
const rows = parse(content, { columns: true, skip_empty_lines: true });
const missing: Record<string, number> = {};
const seen = new Set<string>();
let duplicates = 0;
let invalidDates = 0;
for (const row of rows) {
// Check missing fields
for (const col of columns) {
if (!row[col] || row[col].trim() === '') {
missing[col] = (missing[col] || 0) + 1;
}
}
// Check duplicates (by name/title)
const key = row['Name'] || row['Title'] || row['name'];
if (key && seen.has(key)) duplicates++;
if (key) seen.add(key);
// Check date formats
for (const col of Object.keys(row)) {
if (col.toLowerCase().includes('date') && row[col]) {
if (isNaN(Date.parse(row[col]))) invalidDates++;
}
}
}
return {
totalRecords: rows.length,
recordTypes: { [filePath]: rows.length },
dataQuality: { missingFields: missing, duplicates, invalidDates },
};
}
// Run assessment
const report = assessCSV('legacy-work-orders.csv', ['Title', 'Priority', 'Status']);
console.log('=== Migration Assessment ===');
console.log(`Total records: ${report.totalRecords}`);
console.log('Missing fields:', report.dataQuality.missingFields);
console.log(`Duplicates: ${report.dataQuality.duplicates}`);
console.log(`Invalid dates: ${report.dataQuality.invalidDates}`);
Step 2: Schema Mapping
// src/migration/schema-map.ts
// Map legacy CMMS fields to MaintainX fields
interface FieldMapping {
source: string;
target: string;
transform?: (value: any) => any;
}
const WORK_ORDER_MAP: FieldMapping[] = [
{ source: 'WO_Name', target: 'title' },
{ source: 'WO_Description', target: 'description' },
{
source: 'WO_Priority',
target: 'priority',
transform: (v: string) => {
const map: Record<string, string> = {
'1': 'HIGH', 'Critical': 'HIGH', 'Urgent': 'HIGH',
'2': 'MEDIUM', 'Normal': 'MEDIUM', 'Standard': 'MEDIUM',
'3': 'LOW', 'Low': 'LOW', 'Routine': 'LOW',
};
return map[v] || 'NONE';
},
},
{
source: 'WO_Status',
target: 'status',
transform: (v: string) => {
const map: Record<string, string> = {
'New': 'OPEN', 'Pending': 'OPEN',
'Active': 'IN_PROGRESS', 'Working': 'IN_PROGRESS',
'Waiting': 'ON_HOLD', 'Hold': 'ON_HOLD',
'Done': 'COMPLETED', 'Finished': 'COMPLETED',
'Archived': 'CLOSED', 'Cancelled': 'CLOSED',
};
return map[v] || 'OPEN';
},
},
{
source: 'WO_DueDate',
target: 'dueDate',
transform: (v: string) => v ? new Date(v).toISOString() : undefined,
},
];
const ASSET_MAP: FieldMapping[] = [
{ source: 'Asset_Name', target: 'name' },
{ source: 'Asset_Description', target: 'description' },
{ source: 'Serial_Number', target: 'serialNumber' },
{ source: 'Model_Number', target: 'model' },
{ source: 'Manufacturer', target: 'manufacturer' },
];
function mapRecord(source: Record<string, any>, mappings: FieldMapping[]): Record<string, any> {
const result: Record<string, any> = {};
for (const mapping of mappings) {
let value = source[mapping.source];
if (value !== undefined && value !== '' && mapping.transform) {
value = mapping.transform(value);
}
if (value !== undefined && value !== '') {
result[mapping.target] = value;
}
}
return result;
}
Step 3: ETL Migration
// src/migration/migrate.ts
import { parse } from 'csv-parse/sync';
import { readFileSync, writeFileSync } from 'fs';
import PQueue from 'p-queue';
interface MigrationResult {
success: number;
failed: number;
errors: Array<{ record: any; error: string }>;
}
async function migrateWorkOrders(
client: MaintainXClient,
csvPath: string,
): Promise<MigrationResult> {
const rows = parse(readFileSync(csvPath, 'utf-8'), { columns: true });
const queue = new PQueue({ concurrency: 3, interval: 1000, intervalCap: 5 });
const result: MigrationResult = { success: 0, failed: 0, errors: [] };
console.log(`Migrating ${rows.length} work orders...`);
const promises = rows.map((row: any, index: number) =>
queue.add(async () => {
try {
const mapped = mapRecord(row, WORK_ORDER_MAP);
if (!mapped.title) {
mapped.title = `Migrated WO #${index + 1}`;
}
await client.createWorkOrder(mapped);
result.success++;
if (result.success % 50 === 0) {
console.log(` Progress: ${result.success}/${rows.length}`);
}
} catch (err: any) {
result.failed++;
result.errors.push({
record: row,
error: err.response?.data?.message || err.message,
});
}
}),
);
await Promise.all(promises);
console.log(`\n=== Migration Complete ===`);
console.log(`Success: ${result.success} | Failed: ${result.failed}`);
if (result.errors.length > 0) {
writeFileSync(
'migration-errors.json',
JSON.stringify(result.errors, null, 2),
);
console.log('Errors saved to migration-errors.json');
}
return result;
}
Step 4: Validation and Reconciliation
// src/migration/validate.ts
async function validateMigration(
client: MaintainXClient,
sourceRows: any[],
): Promise<void> {
console.log('=== Migration Validation ===');
// Count comparison
const allWOs = await paginate(
(cursor) => client.getWorkOrders({ limit: 100, cursor }),
'workOrders',
);
console.log(`Source records: ${sourceRows.length}`);
console.log(`MaintainX work orders: ${allWOs.length}`);
console.log(`Match: ${allWOs.length >= sourceRows.length ? 'YES' : 'NO - check migration-errors.json'}`);
// Spot-check random samples
const sampleSize = Math.min(10, allWOs.length);
const samples = allWOs.sort(() => Math.random() - 0.5).slice(0, sampleSize);
console.log(`\nSpot-checking ${sampleSize} random records:`);
for (const wo of samples) {
const checks = [
wo.title ? 'title OK' : 'MISSING title',
wo.priority ? 'priority OK' : 'MISSING priority',
wo.status ? 'status OK' : 'MISSING status',
];
console.log(` #${wo.id}: ${checks.join(', ')}`);
}
}
Rollback Plan
#!/bin/bash
# rollback-migration.sh
# Delete all migrated records (use with extreme caution)
echo "WARNING: This will delete all work orders created during migration."
echo "Press Ctrl+C to cancel, Enter to continue."
read
# Tag migrated work orders with a search pattern
# Then delete by filtering
curl -s "https://api.getmaintainx.com/v1/workorders?limit=100" \
-H "Authorization: Bearer $MAINTAINX_API_KEY" \
| jq -r '.workOrders[] | select(.title | startswith("Migrated")) | .id' \
| while read id; do
echo "Deleting WO #$id..."
curl -s -X DELETE "https://api.getmaintainx.com/v1/workorders/$id" \
-H "Authorization: Bearer $MAINTAINX_API_KEY"
sleep 0.5 # Rate limiting
done
Output
- Source system assessment report (record counts, data quality issues)
- Schema mapping configuration (legacy fields to MaintainX fields)
- ETL migration with rate-limited batch imports
- Validation report comparing source and target counts
- Rollback script for emergency reversal
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| 400 Bad Request on import | Invalid field value after mapping | Fix transform function, re-run failed records |
| 429 during bulk import | Too many records too fast | Reduce PQueue concurrency to 2 |
| Duplicate records | Migration re-run without cleanup | Deduplicate by title or external ID |
| Missing relationships | Assets migrated after work orders | Migrate in order: Locations -> Assets -> Work Orders |
Resources
- MaintainX API Reference
- MaintainX Import Guide
- csv-parse -- CSV parsing for Node.js
Next Steps
You have completed the MaintainX skill pack. For additional support, see the MaintainX Help Center.
Examples
Migrate from Excel spreadsheet:
import XLSX from 'xlsx';
const workbook = XLSX.readFile('maintenance-tracker.xlsx');
const sheet = workbook.Sheets[workbook.SheetNames[0]];
const rows = XLSX.utils.sheet_to_json(sheet);
for (const row of rows) {
const mapped = mapRecord(row as Record<string, any>, WORK_ORDER_MAP);
await client.createWorkOrder(mapped);
}
Migrate locations first, then link assets:
// 1. Migrate locations
const locationIdMap = new Map<string, number>(); // legacy ID → MaintainX ID
for (const loc of legacyLocations) {
const created = await client.request('POST', '/locations', { name: loc.name });
locationIdMap.set(loc.legacyId, created.id);
}
// 2. Migrate assets with location links
for (const asset of legacyAssets) {
const maintainxLocationId = locationIdMa
---
*Content truncated.*
More by jeremylongshore
View all skills by jeremylongshore →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversBoost Payload CMS 3.0 development with validation, querying, and Redis-integrated code generation for efficient project
Enhance your workflow with platform-specific notification sounds on Android. Get alert sounds & custom tones, including
Pica is automated workflow software for business process automation, integrating actions across services via a unified i
Manage projects efficiently with BusinessMap's kanban boards. Track cards, analyze cycles, and streamline workflows—perf
AI Meta MCP Server — AI tool builder for autonomous AI tools and AI function platform: create JS, Python & Shell tools w
Seamlessly integrate your Plane project management platform with 76 tools for efficient task and issue tracking.
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.