Public/Test-KritPax8Mcp.ps1

function Test-KritPax8Mcp {
    <#
    .SYNOPSIS
        Comprehensive health probe for the Pax8 MCP toolkit on this machine.

    .DESCRIPTION
        Runs 7 gates and returns a structured result. Exit non-zero if any FAIL.

            G1 - Secrets folder accessible
            G2 - Token file present + non-empty + sane length
            G3 - OAuth discovery (RFC 8414) endpoint responding correctly
            G4 - MCP initialize JSON-RPC handshake (server identifies itself)
            G5 - tools/list returns >=1 tool (currently 21 Pax8 tools)
            G6 - At least one agent target has the pax8 entry wired
            G7 - The wired entry passes a token-header probe

    .EXAMPLE
        Test-KritPax8Mcp

    .EXAMPLE
        Test-KritPax8Mcp -Quiet
        Returns the result object without writing to host.

    .NOTES
        Author: Joshua Finley - Kritical Pty Ltd
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [string] $SecretsDir,
        [string] $TokenFileName,
        [switch] $Quiet,
        [switch] $NoBanner
    )
    if (-not $NoBanner.IsPresent -and -not $Quiet.IsPresent) {
        Write-KritPax8Banner -Title 'Pax8 MCP Health Probe' -Compact
    }
    $results = [System.Collections.Generic.List[pscustomobject]]::new()
    $addGate = {
        param($name,$pass,$detail)
        $results.Add([pscustomobject]@{ Gate=$name; Pass=[bool]$pass; Detail=$detail })
    }

    # G1
    $tokenPath = Get-KritPax8TokenPath -SecretsDir $SecretsDir -TokenFileName $TokenFileName
    $secretsDirPath = Split-Path -Parent $tokenPath
    $g1 = Test-Path -LiteralPath $secretsDirPath
    & $addGate 'G1.SecretsFolder' $g1 $secretsDirPath

    # G2
    $token = $null
    if ($g1) {
        try { $token = Read-KritPax8Token -SecretsDir $SecretsDir -TokenFileName $TokenFileName } catch { }
    }
    $g2Detail = if ($token) { "length=" + $token.Length } else { "length=0" }
    & $addGate 'G2.TokenSane' ([bool]$token) $g2Detail

    # G3
    $disc = Test-KritPax8McpOAuthDiscovery
    & $addGate 'G3.OAuthDiscovery' $disc.Ok ("issuer=" + $disc.Issuer + " scopes=" + ($disc.Scopes -join ','))

    # G4 + G5 (only if token sane)
    if ($token) {
        $init = Invoke-KritPax8McpInitialize -Token $token
        $g4Detail = "server=" + $init.ServerName + " v" + $init.ServerVersion
        if ($init.Error) { $g4Detail += " err=" + $init.Error }
        & $addGate 'G4.McpInitialize' $init.Ok $g4Detail
        if ($init.Ok) {
            $tl = Get-KritPax8McpToolList -Token $token
            & $addGate 'G5.ToolsList' ($tl.Ok -and $tl.ToolCount -ge 1) ("toolCount=" + $tl.ToolCount + " sample=" + (($tl.Tools | Select-Object -First 5) -join ','))
        } else {
            & $addGate 'G5.ToolsList' $false 'skipped - initialize failed'
        }
    } else {
        & $addGate 'G4.McpInitialize' $false 'skipped - no token'
        & $addGate 'G5.ToolsList'     $false 'skipped - no token'
    }

    # G6
    $status = Get-KritPax8McpStatus -NoBanner
    $wiredAgents = @($status.Agents | Where-Object HasPax8Entry)
    & $addGate 'G6.AnyAgentWired' ($wiredAgents.Count -gt 0) (($wiredAgents | Select-Object -ExpandProperty Agent) -join ',')

    # G7 - re-validate token via the same path the wired agents will use
    if ($token -and $wiredAgents.Count -gt 0) {
        $init2 = Invoke-KritPax8McpInitialize -Token $token
        & $addGate 'G7.WiredAgentTokenValid' $init2.Ok ("status=" + $init2.StatusCode)
    } else {
        & $addGate 'G7.WiredAgentTokenValid' $false 'skipped - no wired agent or no token'
    }

    $passedRows = @($results | Where-Object Pass)
    $failedRows = @($results | Where-Object { -not $_.Pass })

    if (-not $Quiet.IsPresent) {
        $results | Format-Table -AutoSize | Out-String | Write-Host
        if ($failedRows.Count -eq 0) {
            Write-Host ("ALL " + $results.Count + " GATES PASS - Pax8 MCP healthy.") -ForegroundColor Green
        } else {
            Write-Host ($failedRows.Count.ToString() + ' of ' + $results.Count + ' gates FAIL - see Detail.') -ForegroundColor Red
        }
    }

    [pscustomobject]@{
        Gates  = $results
        Passed = $passedRows.Count
        Failed = $failedRows.Count
        Total  = $results.Count
        Ok     = ($failedRows.Count -eq 0)
    }
}