/**
* mcp/skills/repairOutlookDatabase.ts — repair_outlook_database skill
*
* Locates and prepares to run the Microsoft Outlook database repair utility.
* On macOS, identifies the Outlook Database Utility or Profile Manager.
* On Windows, locates scanpst.exe.
* Use when Outlook crashes, hangs, or shows data corruption errors.
*
* Platform strategy
* -----------------
* darwin Checks /Applications/Microsoft Outlook.app/Contents/SharedSupport/
* and ~/Library/Group Containers/UBF8T346G9.Office/ for profile data.
* win32 Searches common scanpst.exe paths and PST/OST files via PowerShell.
*
* Smoke test
* npx tsx -r dotenv/config mcp/skills/repairOutlookDatabase.ts
*/
import * as os from "os" ;
import * as nodePath from "path" ;
import * as fs from "fs/promises" ;
import { exec } from "child_process" ;
import { promisify } from "util" ;
import { z } from "zod" ;
const execAsync = promisify (exec);
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "repair_outlook_database" ,
description:
"Locates and prepares to run the Microsoft Outlook database repair utility. " +
"On macOS, identifies the Outlook Database Utility or Profile Manager. " +
"On Windows, locates scanpst.exe. " +
"Use when Outlook crashes, hangs, or shows data corruption errors." ,
riskLevel: "medium" ,
destructive: false ,
requiresConsent: true ,
supportsDryRun: true ,
affectedScope: [ "user" ],
auditRequired: true ,
schema: {
dryRun: z
. boolean ()
. optional ()
. describe (
"If true, locate repair tool and database files without running repair. Default: true" ,
),
},
} as const ;
// -- Types --------------------------------------------------------------------
interface RepairOutlookResult {
toolFound : boolean ;
toolPath : string | null ;
databaseFiles : string [];
outlookRunning : boolean ;
dryRun : boolean ;
message : string ;
}
// -- PowerShell helper --------------------------------------------------------
async function runPS ( script : string ) : Promise < string > {
const encoded = Buffer. from (script, "utf16le" ). toString ( "base64" );
const { stdout } = await execAsync (
`powershell.exe -NoProfile -NonInteractive -EncodedCommand ${ encoded }` ,
{ maxBuffer: 20 * 1024 * 1024 , timeout: 30_000 },
);
return stdout. trim ();
}
// -- darwin implementation ----------------------------------------------------
async function repairOutlookDarwin ( dryRun : boolean ) : Promise < RepairOutlookResult > {
// Check if Outlook is running
let outlookRunning = false ;
try {
const { stdout } = await execAsync ( "pgrep -x 'Microsoft Outlook'" , { timeout: 3_000 });
outlookRunning = stdout. trim (). length > 0 ;
} catch {
outlookRunning = false ;
}
// Search for Outlook Database Utility
const sharedSupportDir =
"/Applications/Microsoft Outlook.app/Contents/SharedSupport" ;
let toolPath : string | null = null ;
const candidateTools = [
nodePath. join (sharedSupportDir, "Outlook Database Utility.app" ),
nodePath. join (sharedSupportDir, "Outlook Profile Manager.app" ),
];
for ( const candidate of candidateTools) {
try {
await fs. access (candidate);
toolPath = candidate;
break ;
} catch {
// Not found — try next
}
}
// Look for profile/database data
const profileBase = nodePath. join (
os. homedir (),
"Library" ,
"Group Containers" ,
"UBF8T346G9.Office" ,
);
const databaseFiles : string [] = [];
try {
// Check if the base directory exists
await fs. access (profileBase);
databaseFiles. push (profileBase);
// Also look for specific database files
const outlookDataDir = nodePath. join (
profileBase,
"Outlook" ,
"Outlook 15 Profiles" ,
);
try {
await fs. access (outlookDataDir);
databaseFiles. push (outlookDataDir);
} catch {
// Sub-directory may not exist
}
} catch {
// Group container not found — Outlook may not be installed
}
// Open the repair tool if requested
if ( ! dryRun && toolPath) {
try {
await execAsync ( `open "${ toolPath }"` , { timeout: 5_000 });
} catch {
// Non-fatal
}
}
const message = dryRun
? toolPath
? `Found repair tool at: ${ toolPath }. Run with dryRun=false to open it.`
: "Outlook repair tool not found. Ensure Microsoft Outlook is installed."
: toolPath
? `Opened repair tool: ${ toolPath }`
: "Outlook repair tool not found — cannot run repair automatically." ;
return { toolFound: toolPath !== null , toolPath, databaseFiles, outlookRunning, dryRun, message };
}
// -- win32 implementation -----------------------------------------------------
async function repairOutlookWin32 ( dryRun : boolean ) : Promise < RepairOutlookResult > {
// Check if Outlook is running
let outlookRunning = false ;
try {
const raw = await runPS (
`$ErrorActionPreference='SilentlyContinue'; (Get-Process -Name OUTLOOK -ErrorAction SilentlyContinue) -ne $null` ,
);
outlookRunning = raw. trim (). toLowerCase () === "true" ;
} catch {
outlookRunning = false ;
}
// Search common scanpst.exe paths
const scanPstCandidates = [
"C: \\ Program Files \\ Microsoft Office \\ root \\ Office16 \\ SCANPST.EXE" ,
"C: \\ Program Files \\ Microsoft Office \\ root \\ Office15 \\ SCANPST.EXE" ,
"C: \\ Program Files (x86) \\ Microsoft Office \\ root \\ Office16 \\ SCANPST.EXE" ,
"C: \\ Program Files (x86) \\ Microsoft Office \\ root \\ Office15 \\ SCANPST.EXE" ,
"C: \\ Program Files \\ Microsoft Office \\ Office16 \\ SCANPST.EXE" ,
"C: \\ Program Files \\ Microsoft Office \\ Office15 \\ SCANPST.EXE" ,
"C: \\ Program Files (x86) \\ Microsoft Office \\ Office16 \\ SCANPST.EXE" ,
"C: \\ Program Files (x86) \\ Microsoft Office \\ Office15 \\ SCANPST.EXE" ,
];
let toolPath : string | null = null ;
for ( const candidate of scanPstCandidates) {
try {
await fs. access (candidate);
toolPath = candidate;
break ;
} catch {
// Not found — try next
}
}
// Find PST/OST files via PowerShell
let databaseFiles : string [] = [];
try {
const ps = `
$ErrorActionPreference = 'SilentlyContinue'
Get-ChildItem -Path "$env:LOCALAPPDATA \\ Microsoft \\ Outlook" -Include *.pst,*.ost -Recurse |
Select-Object -ExpandProperty FullName | ConvertTo-Json -Compress` . trim ();
const raw = await runPS (ps);
if (raw) {
const parsed = JSON . parse (raw) as string | string [];
databaseFiles = Array. isArray (parsed) ? parsed : [parsed];
}
} catch {
databaseFiles = [];
}
const message = dryRun
? toolPath
? `Found scanpst.exe at: ${ toolPath }. Found ${ databaseFiles . length } data file(s). Run with dryRun=false to open the repair tool.`
: `scanpst.exe not found in standard locations. Found ${ databaseFiles . length } data file(s).`
: toolPath
? `Repair tool located: ${ toolPath }. Run the tool manually against the .pst/.ost files listed in databaseFiles.`
: "scanpst.exe not found — install or repair Microsoft Office to restore the tool." ;
return { toolFound: toolPath !== null , toolPath, databaseFiles, outlookRunning, dryRun, message };
}
// -- Exported run function ----------------------------------------------------
export async function run ({
dryRun = true ,
} : {
dryRun ?: boolean ;
} = {}) {
const platform = os. platform ();
return platform === "win32"
? repairOutlookWin32 (dryRun)
: repairOutlookDarwin (dryRun);
}
// -- Smoke test ---------------------------------------------------------------
if ( false ) {
run ({})
. then ( r => console. log ( JSON . stringify (r, null , 2 )))
. catch (( err : Error ) => { console. error (err.message); process. exit ( 1 ); });
}