Files
gitea-mcp/Program.cs
T
zhengchen.tao 8f35bf5b15
Build Docker Image / build (push) Successful in 10m45s
Build Docker Image / deploy (push) Failing after 53s
fix(http): set CircuitBreaker.SamplingDuration to 60s
Polly StandardResilienceHandler validates SamplingDuration >= 2 * AttemptTimeout
at startup. Default SamplingDuration is 30s and our AttemptTimeout is 30s, so
the container failed to boot with OptionsValidationException.

Set SamplingDuration explicitly to 60s while keeping AttemptTimeout at 30s.
2026-05-16 16:53:18 +08:00

88 lines
4.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using GiteaMcp.Auth;
using GiteaMcp.Config;
using GiteaMcp.Endpoints;
using GiteaMcp.Services;
using Microsoft.Extensions.Http.Resilience;
using System.Net;
using System.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
// ─── 配置绑定 ───────────────────────────────────────────────
builder.Services.Configure<GiteaOptions>(
builder.Configuration.GetSection(GiteaOptions.SectionName));
builder.Services.Configure<JwtOptions>(
builder.Configuration.GetSection(JwtOptions.SectionName));
builder.Services.Configure<McpDiscoveryOptions>(
builder.Configuration.GetSection(McpDiscoveryOptions.SectionName));
// ─── JWT Bearer + Scope Policy ─────────────────────────────
builder.Services.AddGiteaJwtBearer(builder.Configuration);
builder.Services.AddScopePolicies();
// ─── HTTP Context AccessorTool 里可选用,暂保留接口) ────────
builder.Services.AddHttpContextAccessor();
// ─── Gitea HTTP Client ─────────────────────────────────────
var giteaBaseUrl = builder.Configuration["Gitea:BaseUrl"]
?? "https://git.zhengchentao.win";
var giteaPat = builder.Configuration["Gitea:AdminPat"] ?? string.Empty;
builder.Services.AddHttpClient("gitea", client =>
{
// 确保 BaseAddress 末尾有斜杠(HttpClient 的规范)
var url = giteaBaseUrl.TrimEnd('/') + "/";
client.BaseAddress = new Uri(url);
// Gitea 推荐 "token <PAT>" 格式,比 Bearer 更稳
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("token", giteaPat);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
// 单请求超时 30s
client.Timeout = TimeSpan.FromSeconds(30);
})
.AddStandardResilienceHandler(options =>
{
// 3 次重试,指数退避(Microsoft.Extensions.Http.Resilience 标准配置)
options.Retry.MaxRetryAttempts = 3;
options.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
options.Retry.Delay = TimeSpan.FromSeconds(1);
options.Retry.UseJitter = true;
// 仅对 5xx / 429 / 网络错误重试;4xx 由 ShouldHandle 默认配置自动跳过
options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(90);
options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(30);
// Polly 校验:SamplingDuration ≥ 2× AttemptTimeout,默认 30s 不达标
options.CircuitBreaker.SamplingDuration = TimeSpan.FromSeconds(60);
});
// ─── 业务服务 ──────────────────────────────────────────────
builder.Services.AddSingleton<GiteaRepoFilter>();
builder.Services.AddScoped<GiteaApiClient>();
// ─── MCP Server ────────────────────────────────────────────
builder.Services.AddMcpServer()
.WithHttpTransport() // Streamable HTTPClaude.ai custom connector 走这个)
.WithToolsFromAssembly(); // 自动扫描 [McpServerToolType]
// ─── Build ─────────────────────────────────────────────────
var app = builder.Build();
// ─── Middleware 顺序:认证 → 授权 → 路由 ────────────────────
app.UseAuthentication();
app.UseAuthorization();
// ─── Endpoints ─────────────────────────────────────────────
app.MapDiscovery();
// MCP 端点:要求通过 JWT 认证 + read:gitea scope
app.MapMcp("/mcp")
.RequireAuthorization(ScopePolicies.ReadGitea);
// 健康检查(Kubernetes / Docker healthcheck 用)
app.MapGet("/healthz", () => Results.Ok(new { status = "ok", timestamp = DateTimeOffset.UtcNow }));
app.Run();