Skip to content

Commit 84439f5

Browse files
committed
feat(12-02): catch BrokenCircuitException in McpToolDispatcher, rethrow as McpException (ROB-03)
1 parent bf1d0ea commit 84439f5

1 file changed

Lines changed: 25 additions & 15 deletions

File tree

src/GsdOrchestrator/Mcp/McpToolDispatcher.cs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
using System.Text.Json.Nodes;
22
using Microsoft.Extensions.Logging;
33
using Polly;
4+
using Polly.CircuitBreaker;
45
using Polly.Registry;
56

67
namespace GsdOrchestrator.Mcp;
78

89
/// <summary>
9-
/// Wraps IMcpClient with resilience (Polly retry), logging, and secondary rate limit
10-
/// special-casing. Inject and use this instead of calling IMcpClient directly.
10+
/// Wraps IMcpClient with resilience (Polly retry + circuit breaker), logging, and secondary
11+
/// rate limit special-casing. Inject and use this instead of calling IMcpClient directly.
1112
/// </summary>
1213
public sealed class McpToolDispatcher
1314
{
@@ -28,21 +29,30 @@ public McpToolDispatcher(
2829
public async Task<McpToolResult> CallAsync(
2930
string tool, JsonObject args, CancellationToken ct = default)
3031
{
31-
return await _pipeline.ExecuteAsync(async token =>
32+
try
3233
{
33-
try
34+
return await _pipeline.ExecuteAsync(async token =>
3435
{
35-
return await _client.CallToolAsync(tool, args, token);
36-
}
37-
catch (McpException ex) when (ex.IsSecondaryRateLimit)
38-
{
39-
// Secondary rate limit: MUST wait at least 60 seconds — bypass Polly retry
40-
_logger.LogWarning(
41-
"Secondary rate limit on '{Tool}'. Waiting 65s before retry.", tool);
42-
await Task.Delay(TimeSpan.FromSeconds(65), token);
43-
return await _client.CallToolAsync(tool, args, token);
44-
}
45-
}, ct);
36+
try
37+
{
38+
return await _client.CallToolAsync(tool, args, token);
39+
}
40+
catch (McpException ex) when (ex.IsSecondaryRateLimit)
41+
{
42+
// Secondary rate limit: MUST wait at least 60 seconds — bypass Polly retry
43+
_logger.LogWarning(
44+
"Secondary rate limit on '{Tool}'. Waiting 65s before retry.", tool);
45+
await Task.Delay(TimeSpan.FromSeconds(65), token);
46+
return await _client.CallToolAsync(tool, args, token);
47+
}
48+
}, ct);
49+
}
50+
catch (BrokenCircuitException)
51+
{
52+
throw new McpException(
53+
"MCP circuit breaker open — too many consecutive failures",
54+
isTransient: false);
55+
}
4656
}
4757

4858
public Task<IReadOnlyList<McpTool>> ListToolsAsync(CancellationToken ct = default) =>

0 commit comments

Comments
 (0)