Skip to content

Commit 0d8c8df

Browse files
gfraiteurclaude
andcommitted
Refactor MCP configuration to use --mcp-config flag
Pass MCP port directly to RunClaude.ps1 instead of using environment variables. Configure MCP server via temporary config file and Claude's --mcp-config flag for more reliable SSE transport setup. Move port file to TEMP directory. Add debug logging to RiskAnalyzer for troubleshooting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 0bbbebd commit 0d8c8df

File tree

5 files changed

+97
-108
lines changed

5 files changed

+97
-108
lines changed

DockerBuild.ps1

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -490,37 +490,6 @@ foreach (`$dir in `$gitDirectories) {
490490
}
491491
}
492492
493-
# Configure MCP approval server if available (for Claude mode)
494-
`$mcpServerUrl = [Environment]::GetEnvironmentVariable('MCP_APPROVAL_SERVER')
495-
if (`$mcpServerUrl) {
496-
Write-Host "Configuring MCP approval server: `$mcpServerUrl" -ForegroundColor Cyan
497-
498-
# Read existing settings or create new
499-
`$settingsPath = "`$env:USERPROFILE\.claude\settings.json"
500-
`$settingsDir = Split-Path `$settingsPath -Parent
501-
if (-not (Test-Path `$settingsDir)) {
502-
New-Item -ItemType Directory -Path `$settingsDir -Force | Out-Null
503-
}
504-
505-
if (Test-Path `$settingsPath) {
506-
`$settings = Get-Content `$settingsPath -Raw | ConvertFrom-Json -AsHashtable
507-
} else {
508-
`$settings = @{}
509-
}
510-
511-
# Add MCP server configuration
512-
if (-not `$settings.ContainsKey('mcpServers')) {
513-
`$settings['mcpServers'] = @{}
514-
}
515-
`$settings['mcpServers']['host-approval'] = @{
516-
'type' = 'http'
517-
'url' = `$mcpServerUrl
518-
}
519-
520-
# Write updated settings
521-
`$settings | ConvertTo-Json -Depth 10 | Set-Content `$settingsPath -Encoding UTF8
522-
Write-Host "MCP server configured in Claude settings" -ForegroundColor Green
523-
}
524493
"@
525494
$initScriptContent | Set-Content -Path $initScript -Encoding UTF8
526495

@@ -593,10 +562,7 @@ if (-not $BuildImage)
593562
$mcpPortFile = $null
594563
if (-not $NoMcp) {
595564
Write-Host "Starting MCP approval server..." -ForegroundColor Green
596-
$mcpPortFile = Join-Path $PSScriptRoot $dockerContextDirectory "mcp-port.txt"
597-
if (Test-Path $mcpPortFile) {
598-
Remove-Item $mcpPortFile
599-
}
565+
$mcpPortFile = Join-Path $env:TEMP "mcp-port-$([System.Guid]::NewGuid().ToString('N').Substring(0,8)).txt"
600566

601567
# Build the command to run in the new tab
602568
$mcpCommand = "& '$SourceDirName\Build.ps1' tools mcp-server --port-file '$mcpPortFile'"
@@ -690,30 +656,27 @@ if (-not $BuildImage)
690656
{
691657
# Non-interactive mode with prompt - no -it flags
692658
$dockerArgs = @()
693-
$inlineScript = "${substCommandsInline}& c:\Init.g.ps1; ${copyClaudeJsonScript}cd '$SourceDirName'; & .\eng\RunClaude.ps1 -Prompt `"$ClaudePrompt`""
659+
$mcpArg = if ($mcpPort) { " -McpPort $mcpPort" } else { "" }
660+
$inlineScript = "${substCommandsInline}& c:\Init.g.ps1; ${copyClaudeJsonScript}cd '$SourceDirName'; & .\eng\RunClaude.ps1 -Prompt `"$ClaudePrompt`"$mcpArg"
694661
}
695662
else
696663
{
697664
# Interactive mode - requires TTY
698665
$dockerArgs = @("-it")
699-
$inlineScript = "${substCommandsInline}& c:\Init.g.ps1; ${copyClaudeJsonScript}cd '$SourceDirName'; & .\eng\RunClaude.ps1"
666+
$mcpArg = if ($mcpPort) { " -McpPort $mcpPort" } else { "" }
667+
$inlineScript = "${substCommandsInline}& c:\Init.g.ps1; ${copyClaudeJsonScript}cd '$SourceDirName'; & .\eng\RunClaude.ps1$mcpArg"
700668
}
701669

702670
$dockerArgsAsString = $dockerArgs -join " "
703671
$pwshPath = 'C:\Program Files\PowerShell\7\pwsh.exe'
704672

705673
# Set HOME/USERPROFILE so Claude finds its config in the mounted location
706-
# Also pass MCP approval server URL if available
707674
$envArgs = @(
708675
"-e", "HOME=$containerUserProfile",
709676
"-e", "USERPROFILE=$containerUserProfile"
710677
)
711-
if ($mcpPort) {
712-
$envArgs += @("-e", "MCP_APPROVAL_SERVER=http://host.docker.internal:$mcpPort")
713-
}
714678

715-
$mcpEnvDisplay = if ($mcpPort) { " -e MCP_APPROVAL_SERVER=http://host.docker.internal:$mcpPort" } else { "" }
716-
Write-Host "Executing: ``docker run --rm --memory=12g $dockerArgsAsString $VolumeMappingsAsString -e HOME=$containerUserProfile -e USERPROFILE=$containerUserProfile$mcpEnvDisplay -w $ContainerSourceDir $ImageTag `"$pwshPath`" -Command `"$inlineScript`"" -ForegroundColor Cyan
679+
Write-Host "Executing: ``docker run --rm --memory=12g $dockerArgsAsString $VolumeMappingsAsString -e HOME=$containerUserProfile -e USERPROFILE=$containerUserProfile -w $ContainerSourceDir $ImageTag `"$pwshPath`" -Command `"$inlineScript`"" -ForegroundColor Cyan
717680

718681
try {
719682
docker run --rm --memory=12g $dockerArgs @VolumeMappings @envArgs -w $ContainerSourceDir $ImageTag $pwshPath -Command $inlineScript

eng/RunClaude.ps1

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# You can generate this file using `./Build.ps1 generate-scripts`.
33

44
param(
5-
[string]$Prompt
5+
[string]$Prompt,
6+
[int]$McpPort
67
)
78

89
$ErrorActionPreference = "Stop"
@@ -13,18 +14,41 @@ if ($env:RUNNING_IN_DOCKER -ne "true")
1314
exit 1
1415
}
1516

17+
# Configure MCP approval server if port is specified
18+
$mcpConfigArg = ""
19+
if ($McpPort -gt 0) {
20+
$sseUrl = "http://host.docker.internal:$McpPort/sse"
21+
Write-Host "Configuring MCP approval server: $sseUrl" -ForegroundColor Cyan
22+
23+
# Create temporary MCP config file
24+
$mcpConfigPath = "$env:TEMP\mcp-config.json"
25+
$mcpConfig = @{
26+
'mcpServers' = @{
27+
'host-approval' = @{
28+
'type' = 'sse'
29+
'url' = $sseUrl
30+
}
31+
}
32+
}
33+
$mcpConfig | ConvertTo-Json -Depth 10 | Set-Content $mcpConfigPath -Encoding UTF8
34+
$mcpConfigArg = "--mcp-config `"$mcpConfigPath`""
35+
Write-Host "MCP config file created: $mcpConfigPath" -ForegroundColor Green
36+
}
37+
1638
Write-Host "Starting Claude CLI..." -ForegroundColor Green
1739

1840
# Run Claude
1941
if ($Prompt)
2042
{
2143
Write-Host "Running Claude with prompt: $Prompt" -ForegroundColor Cyan
22-
claude --dangerously-skip-permissions -p $Prompt
44+
$cmd = "claude --dangerously-skip-permissions $mcpConfigArg -p `"$Prompt`""
45+
Invoke-Expression $cmd
2346
}
2447
else
2548
{
2649
Write-Host "Running Claude in interactive mode" -ForegroundColor Cyan
27-
claude --dangerously-skip-permissions
50+
$cmd = "claude --dangerously-skip-permissions $mcpConfigArg"
51+
Invoke-Expression $cmd
2852
}
2953

3054
exit $LASTEXITCODE

src/PostSharp.Engineering.BuildTools/Mcp/Services/RiskAnalyzer.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details.
22

33
using PostSharp.Engineering.BuildTools.Mcp.Models;
4+
using Spectre.Console;
45
using System;
56
using System.Collections.Generic;
67
using System.Diagnostics;
@@ -142,10 +143,14 @@ public async Task<RiskAssessment> AnalyzeAsync(
142143
using var timeoutCts = new CancellationTokenSource( TimeSpan.FromSeconds( 120 ) );
143144
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( cancellationToken, timeoutCts.Token );
144145

146+
var escapedPrompt = EscapeForShell( prompt );
147+
AnsiConsole.MarkupLine( "[dim]Starting Claude CLI for risk analysis...[/]" );
148+
AnsiConsole.MarkupLine( $"[dim]Prompt length: {prompt.Length} chars[/]" );
149+
145150
var startInfo = new ProcessStartInfo
146151
{
147152
FileName = "claude",
148-
Arguments = $"-p \"{EscapeForShell( prompt )}\"",
153+
Arguments = $"-p \"{escapedPrompt}\"",
149154
RedirectStandardOutput = true,
150155
RedirectStandardError = true,
151156
UseShellExecute = false,
@@ -156,25 +161,50 @@ public async Task<RiskAssessment> AnalyzeAsync(
156161

157162
if ( process == null )
158163
{
164+
AnsiConsole.MarkupLine( "[red]Failed to start Claude CLI process[/]" );
165+
159166
return RiskAssessment.Default( "Failed to start Claude CLI for analysis" );
160167
}
161168

169+
AnsiConsole.MarkupLine( $"[dim]Claude CLI started (PID: {process.Id})[/]" );
170+
162171
var output = await process.StandardOutput.ReadToEndAsync( linkedCts.Token );
172+
var stderr = await process.StandardError.ReadToEndAsync( linkedCts.Token );
163173
await process.WaitForExitAsync( linkedCts.Token );
164174

175+
AnsiConsole.MarkupLine( $"[dim]Claude CLI exited with code: {process.ExitCode}[/]" );
176+
177+
if ( !string.IsNullOrWhiteSpace( stderr ) )
178+
{
179+
AnsiConsole.MarkupLine( $"[yellow]Claude CLI stderr:[/]" );
180+
AnsiConsole.WriteLine( stderr );
181+
}
182+
183+
if ( !string.IsNullOrWhiteSpace( output ) )
184+
{
185+
AnsiConsole.MarkupLine( $"[dim]Claude CLI output:[/]" );
186+
AnsiConsole.WriteLine( output.Length > 500 ? output[..500] + "..." : output );
187+
}
188+
165189
if ( process.ExitCode != 0 )
166190
{
191+
AnsiConsole.MarkupLine( $"[red]Claude CLI failed with exit code {process.ExitCode}[/]" );
192+
167193
return RiskAssessment.Default( "Claude CLI analysis failed" );
168194
}
169195

170196
return RiskAssessment.Parse( output );
171197
}
172198
catch ( OperationCanceledException )
173199
{
200+
AnsiConsole.MarkupLine( "[yellow]Claude CLI analysis timed out[/]" );
201+
174202
return RiskAssessment.Default( "Analysis timed out - human review required" );
175203
}
176204
catch ( Exception ex )
177205
{
206+
AnsiConsole.MarkupLine( $"[red]Claude CLI error: {ex.Message}[/]" );
207+
178208
return RiskAssessment.Default( $"Analysis error: {ex.Message}" );
179209
}
180210
}

src/PostSharp.Engineering.BuildTools/Resources/DockerBuild.ps1

Lines changed: 6 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -490,52 +490,6 @@ foreach (`$dir in `$gitDirectories) {
490490
}
491491
}
492492
493-
# Configure MCP approval server if available (for Claude mode)
494-
`$mcpServerUrl = [Environment]::GetEnvironmentVariable('MCP_APPROVAL_SERVER')
495-
if (`$mcpServerUrl) {
496-
Write-Host "Configuring MCP approval server: `$mcpServerUrl" -ForegroundColor Cyan
497-
498-
# Use claude mcp add command to properly register the server
499-
# This ensures the configuration is in the correct format
500-
`$sseUrl = "`$mcpServerUrl/sse"
501-
502-
# Remove existing host-approval server if present (ignore errors)
503-
& claude mcp remove host-approval 2>`$null
504-
505-
# Add the MCP server using the CLI
506-
& claude mcp add host-approval --transport http `$sseUrl
507-
508-
if (`$LASTEXITCODE -eq 0) {
509-
Write-Host "MCP server configured via 'claude mcp add'" -ForegroundColor Green
510-
} else {
511-
Write-Host "Warning: Failed to configure MCP server via CLI, trying settings.json fallback" -ForegroundColor Yellow
512-
513-
# Fallback: write to settings.json directly
514-
`$settingsPath = "`$env:USERPROFILE\.claude\settings.json"
515-
`$settingsDir = Split-Path `$settingsPath -Parent
516-
if (-not (Test-Path `$settingsDir)) {
517-
New-Item -ItemType Directory -Path `$settingsDir -Force | Out-Null
518-
}
519-
520-
if (Test-Path `$settingsPath) {
521-
`$settings = Get-Content `$settingsPath -Raw | ConvertFrom-Json -AsHashtable
522-
} else {
523-
`$settings = @{}
524-
}
525-
526-
# Add MCP server configuration with SSE URL
527-
if (-not `$settings.ContainsKey('mcpServers')) {
528-
`$settings['mcpServers'] = @{}
529-
}
530-
`$settings['mcpServers']['host-approval'] = @{
531-
'url' = `$sseUrl
532-
}
533-
534-
# Write updated settings
535-
`$settings | ConvertTo-Json -Depth 10 | Set-Content `$settingsPath -Encoding UTF8
536-
Write-Host "MCP server configured in Claude settings.json" -ForegroundColor Green
537-
}
538-
}
539493
"@
540494
$initScriptContent | Set-Content -Path $initScript -Encoding UTF8
541495

@@ -608,10 +562,7 @@ if (-not $BuildImage)
608562
$mcpPortFile = $null
609563
if (-not $NoMcp) {
610564
Write-Host "Starting MCP approval server..." -ForegroundColor Green
611-
$mcpPortFile = Join-Path $PSScriptRoot $dockerContextDirectory "mcp-port.txt"
612-
if (Test-Path $mcpPortFile) {
613-
Remove-Item $mcpPortFile
614-
}
565+
$mcpPortFile = Join-Path $env:TEMP "mcp-port-$([System.Guid]::NewGuid().ToString('N').Substring(0,8)).txt"
615566

616567
# Build the command to run in the new tab
617568
$mcpCommand = "& '$SourceDirName\Build.ps1' tools mcp-server --port-file '$mcpPortFile'"
@@ -705,30 +656,27 @@ if (-not $BuildImage)
705656
{
706657
# Non-interactive mode with prompt - no -it flags
707658
$dockerArgs = @()
708-
$inlineScript = "${substCommandsInline}& c:\Init.g.ps1; ${copyClaudeJsonScript}cd '$SourceDirName'; & .\eng\RunClaude.ps1 -Prompt `"$ClaudePrompt`""
659+
$mcpArg = if ($mcpPort) { " -McpPort $mcpPort" } else { "" }
660+
$inlineScript = "${substCommandsInline}& c:\Init.g.ps1; ${copyClaudeJsonScript}cd '$SourceDirName'; & .\eng\RunClaude.ps1 -Prompt `"$ClaudePrompt`"$mcpArg"
709661
}
710662
else
711663
{
712664
# Interactive mode - requires TTY
713665
$dockerArgs = @("-it")
714-
$inlineScript = "${substCommandsInline}& c:\Init.g.ps1; ${copyClaudeJsonScript}cd '$SourceDirName'; & .\eng\RunClaude.ps1"
666+
$mcpArg = if ($mcpPort) { " -McpPort $mcpPort" } else { "" }
667+
$inlineScript = "${substCommandsInline}& c:\Init.g.ps1; ${copyClaudeJsonScript}cd '$SourceDirName'; & .\eng\RunClaude.ps1$mcpArg"
715668
}
716669

717670
$dockerArgsAsString = $dockerArgs -join " "
718671
$pwshPath = 'C:\Program Files\PowerShell\7\pwsh.exe'
719672

720673
# Set HOME/USERPROFILE so Claude finds its config in the mounted location
721-
# Also pass MCP approval server URL if available
722674
$envArgs = @(
723675
"-e", "HOME=$containerUserProfile",
724676
"-e", "USERPROFILE=$containerUserProfile"
725677
)
726-
if ($mcpPort) {
727-
$envArgs += @("-e", "MCP_APPROVAL_SERVER=http://host.docker.internal:$mcpPort")
728-
}
729678

730-
$mcpEnvDisplay = if ($mcpPort) { " -e MCP_APPROVAL_SERVER=http://host.docker.internal:$mcpPort" } else { "" }
731-
Write-Host "Executing: ``docker run --rm --memory=12g $dockerArgsAsString $VolumeMappingsAsString -e HOME=$containerUserProfile -e USERPROFILE=$containerUserProfile$mcpEnvDisplay -w $ContainerSourceDir $ImageTag `"$pwshPath`" -Command `"$inlineScript`"" -ForegroundColor Cyan
679+
Write-Host "Executing: ``docker run --rm --memory=12g $dockerArgsAsString $VolumeMappingsAsString -e HOME=$containerUserProfile -e USERPROFILE=$containerUserProfile -w $ContainerSourceDir $ImageTag `"$pwshPath`" -Command `"$inlineScript`"" -ForegroundColor Cyan
732680

733681
try {
734682
docker run --rm --memory=12g $dockerArgs @VolumeMappings @envArgs -w $ContainerSourceDir $ImageTag $pwshPath -Command $inlineScript

src/PostSharp.Engineering.BuildTools/Resources/RunClaude.ps1

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# You can generate this file using `./Build.ps1 generate-scripts`.
33

44
param(
5-
[string]$Prompt
5+
[string]$Prompt,
6+
[int]$McpPort
67
)
78

89
$ErrorActionPreference = "Stop"
@@ -13,18 +14,41 @@ if ($env:RUNNING_IN_DOCKER -ne "true")
1314
exit 1
1415
}
1516

17+
# Configure MCP approval server if port is specified
18+
$mcpConfigArg = ""
19+
if ($McpPort -gt 0) {
20+
$sseUrl = "http://host.docker.internal:$McpPort/sse"
21+
Write-Host "Configuring MCP approval server: $sseUrl" -ForegroundColor Cyan
22+
23+
# Create temporary MCP config file
24+
$mcpConfigPath = "$env:TEMP\mcp-config.json"
25+
$mcpConfig = @{
26+
'mcpServers' = @{
27+
'host-approval' = @{
28+
'type' = 'sse'
29+
'url' = $sseUrl
30+
}
31+
}
32+
}
33+
$mcpConfig | ConvertTo-Json -Depth 10 | Set-Content $mcpConfigPath -Encoding UTF8
34+
$mcpConfigArg = "--mcp-config `"$mcpConfigPath`""
35+
Write-Host "MCP config file created: $mcpConfigPath" -ForegroundColor Green
36+
}
37+
1638
Write-Host "Starting Claude CLI..." -ForegroundColor Green
1739

1840
# Run Claude
1941
if ($Prompt)
2042
{
2143
Write-Host "Running Claude with prompt: $Prompt" -ForegroundColor Cyan
22-
claude --dangerously-skip-permissions -p $Prompt
44+
$cmd = "claude --dangerously-skip-permissions $mcpConfigArg -p `"$Prompt`""
45+
Invoke-Expression $cmd
2346
}
2447
else
2548
{
2649
Write-Host "Running Claude in interactive mode" -ForegroundColor Cyan
27-
claude --dangerously-skip-permissions
50+
$cmd = "claude --dangerously-skip-permissions $mcpConfigArg"
51+
Invoke-Expression $cmd
2852
}
2953

3054
exit $LASTEXITCODE

0 commit comments

Comments
 (0)