Skip to content

Commit 34f490e

Browse files
gfraiteurclaude
andcommitted
Add concurrency protection for MCP approval requests
Implement a semaphore-based lock to ensure only one approval request can be processed at a time. If a second request arrives while the first is pending, it will be immediately rejected with the message: "Another approval request is being processed. Wait until this request is approved." This prevents: - Confusion from multiple overlapping approval prompts - Race conditions in the approval flow - Concurrent access to the approval UI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
1 parent 8970361 commit 34f490e

File tree

1 file changed

+33
-13
lines changed

1 file changed

+33
-13
lines changed

src/PostSharp.Engineering.BuildTools/Mcp/Tools/ExecuteCommandTool.cs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ namespace PostSharp.Engineering.BuildTools.Mcp.Tools;
1717
[McpServerToolType]
1818
public sealed class ExecuteCommandTool
1919
{
20+
private static readonly SemaphoreSlim _approvalLock = new( 1, 1 );
21+
2022
private readonly CommandHistoryService _history;
2123
private readonly RiskAnalyzer _analyzer;
2224
private readonly RegexRuleEngine _regexEngine;
@@ -51,17 +53,29 @@ public async Task<CommandResult> ExecuteCommand(
5153
// Use a constant session ID for single session model
5254
const string sessionId = "default";
5355

54-
// Log incoming request
55-
AnsiConsole.WriteLine();
56-
AnsiConsole.Write( new Rule( "[yellow]Incoming Command Request[/]" ) );
57-
AnsiConsole.MarkupLine( $"[dim]Time:[/] {DateTime.Now:yyyy-MM-dd HH:mm:ss}" );
58-
AnsiConsole.MarkupLine( $"[dim]Command:[/] [white]{command.EscapeMarkup()}[/]" );
59-
AnsiConsole.MarkupLine( $"[dim]Working Directory:[/] {workingDirectory.EscapeMarkup()}" );
60-
AnsiConsole.MarkupLine( $"[dim]Purpose:[/] {claimedPurpose.EscapeMarkup()}" );
61-
AnsiConsole.WriteLine();
56+
// Check if another approval request is already in progress
57+
if ( !await _approvalLock.WaitAsync( 0, cancellationToken ) )
58+
{
59+
AnsiConsole.WriteLine();
60+
AnsiConsole.MarkupLine( "[red]Another approval request is being processed. Wait until this request is approved.[/]" );
61+
AnsiConsole.WriteLine();
62+
63+
return CommandResult.Error( "Another approval request is being processed. Wait until this request is approved." );
64+
}
6265

6366
try
6467
{
68+
// Log incoming request
69+
AnsiConsole.WriteLine();
70+
AnsiConsole.Write( new Rule( "[yellow]Incoming Command Request[/]" ) );
71+
AnsiConsole.MarkupLine( $"[dim]Time:[/] {DateTime.Now:yyyy-MM-dd HH:mm:ss}" );
72+
AnsiConsole.MarkupLine( $"[dim]Command:[/] [white]{command.EscapeMarkup()}[/]" );
73+
AnsiConsole.MarkupLine( $"[dim]Working Directory:[/] {workingDirectory.EscapeMarkup()}" );
74+
AnsiConsole.MarkupLine( $"[dim]Purpose:[/] {claimedPurpose.EscapeMarkup()}" );
75+
AnsiConsole.WriteLine();
76+
77+
try
78+
{
6579
// 1. Get session history
6680
var sessionHistory = this._history.GetHistory( sessionId );
6781

@@ -124,13 +138,19 @@ public async Task<CommandResult> ExecuteCommand(
124138
this._history.Record( sessionId, command, claimedPurpose, approved, result );
125139

126140
return result;
141+
}
142+
catch ( Exception ex )
143+
{
144+
AnsiConsole.MarkupLine( $"[red]Error: {ex.Message.EscapeMarkup()}[/]" );
145+
AnsiConsole.WriteException( ex );
146+
147+
return CommandResult.Error( ex.Message );
148+
}
127149
}
128-
catch ( Exception ex )
150+
finally
129151
{
130-
AnsiConsole.MarkupLine( $"[red]Error: {ex.Message.EscapeMarkup()}[/]" );
131-
AnsiConsole.WriteException( ex );
132-
133-
return CommandResult.Error( ex.Message );
152+
// Always release the lock when done
153+
_approvalLock.Release();
134154
}
135155
}
136156
}

0 commit comments

Comments
 (0)