From d98600a3194dafbd38f74ef69a3f82d40da11f6f Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Wed, 25 Mar 2026 12:55:17 -0400 Subject: [PATCH] fix: MCP bridge built, signed, and shipped in CI releases - Removed useless CLAUDE_MCP_SERVERS env var injection (doesn't work) - CI builds wraith-mcp-bridge.exe as a separate cargo --bin step - Bridge binary signed with EV cert alongside the installer - Uploaded to Gitea packages per version - Attached to Gitea release as a downloadable asset - Users add to PATH then: claude mcp add wraith -- wraith-mcp-bridge Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitea/workflows/build-release.yml | 33 ++++++++++++++++++++++++++++-- .gitignore | 1 + src-tauri/src/pty/mod.rs | 7 +------ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/build-release.yml b/.gitea/workflows/build-release.yml index 96ab3f0..d28e780 100644 --- a/.gitea/workflows/build-release.yml +++ b/.gitea/workflows/build-release.yml @@ -71,6 +71,15 @@ jobs: Write-Host "=== Build output ===" Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\* + - name: Build and package MCP bridge binary + shell: powershell + run: | + $env:Path = "$env:EXTRA_PATH;$env:Path" + cd src-tauri + cargo build --release --bin wraith-mcp-bridge + Write-Host "Bridge binary built:" + Get-ChildItem target\release\wraith-mcp-bridge.exe + - name: Download jsign shell: powershell run: | @@ -95,7 +104,10 @@ jobs: run: | $env:Path = "$env:EXTRA_PATH;$env:Path" $token = [System.IO.File]::ReadAllText("$env:TEMP\aztoken.txt") - $binaries = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe + # Sign NSIS installers + MCP bridge binary + $binaries = @() + $binaries += Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe + $binaries += Get-Item src-tauri\target\release\wraith-mcp-bridge.exe -ErrorAction SilentlyContinue foreach ($binary in $binaries) { Write-Host "Signing: $($binary.FullName)" java -jar jsign.jar --storetype AZUREKEYVAULT --keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" --storepass $token --alias "${{ secrets.AZURE_CERT_NAME }}" --tsaurl http://timestamp.digicert.com --tsmode RFC3161 $binary.FullName @@ -110,6 +122,13 @@ jobs: $giteaUrl = "https://git.command.vigilcyber.com" $headers = @{ Authorization = "token ${{ secrets.GIT_TOKEN }}" } + # Upload MCP bridge binary + $bridge = "src-tauri\target\release\wraith-mcp-bridge.exe" + if (Test-Path $bridge) { + Write-Host "Uploading: wraith-mcp-bridge.exe" + Invoke-RestMethod -Uri "$giteaUrl/api/packages/vstockwell/generic/wraith/$ver/wraith-mcp-bridge.exe" -Method PUT -Headers $headers -ContentType "application/octet-stream" -InFile $bridge + } + $installers = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe foreach ($file in $installers) { $hash = (Get-FileHash $file.FullName -Algorithm SHA256).Hash.ToLower() @@ -184,10 +203,20 @@ jobs: $releaseId = $release.id Write-Host "Release v$ver created (id: $releaseId)" - $installers = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe $uploadHeaders = @{ Authorization = "token ${{ secrets.GIT_TOKEN }}" } + + # Attach installer(s) + $installers = Get-ChildItem -Recurse src-tauri\target\release\bundle\nsis\*.exe foreach ($file in $installers) { Write-Host "Attaching $($file.Name) to release..." Invoke-RestMethod -Uri "$giteaUrl/api/v1/repos/vstockwell/wraith/releases/$releaseId/assets?name=$($file.Name)" -Method POST -Headers $uploadHeaders -ContentType "application/octet-stream" -InFile $file.FullName Write-Host "Attached: $($file.Name)" } + + # Attach MCP bridge binary + $bridge = "src-tauri\target\release\wraith-mcp-bridge.exe" + if (Test-Path $bridge) { + Write-Host "Attaching wraith-mcp-bridge.exe to release..." + Invoke-RestMethod -Uri "$giteaUrl/api/v1/repos/vstockwell/wraith/releases/$releaseId/assets?name=wraith-mcp-bridge.exe" -Method POST -Headers $uploadHeaders -ContentType "application/octet-stream" -InFile $bridge + Write-Host "Attached: wraith-mcp-bridge.exe" + } diff --git a/.gitignore b/.gitignore index 564d1c1..1ce53e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ dist/ src-tauri/target/ +src-tauri/binaries/ *.log .DS_Store diff --git a/src-tauri/src/pty/mod.rs b/src-tauri/src/pty/mod.rs index 8341b9d..667cd35 100644 --- a/src-tauri/src/pty/mod.rs +++ b/src-tauri/src/pty/mod.rs @@ -96,12 +96,7 @@ impl PtyService { .openpty(PtySize { rows, cols, pixel_width: 0, pixel_height: 0 }) .map_err(|e| format!("Failed to open PTY: {}", e))?; - let mut cmd = CommandBuilder::new(shell_path); - - // Auto-inject MCP server config so AI CLIs discover the bridge. - // Claude Code reads CLAUDE_MCP_SERVERS env var for server config. - let mcp_config = r#"{"wraith":{"command":"wraith-mcp-bridge","args":[]}}"#; - cmd.env("CLAUDE_MCP_SERVERS", mcp_config); + let cmd = CommandBuilder::new(shell_path); let child = pair.slave .spawn_command(cmd)