# Svrnty.MCP.Gateway - API Reference **Version:** 1.0.0 **Last Updated:** 2025-10-19 **Status:** Production-Ready --- ## Table of Contents - [Core Abstractions](#core-abstractions) - [IGatewayRouter](#igatewayrouter) - [IRoutingStrategy](#iroutingstrategy) - [ICircuitBreaker](#icircuitbreaker) - [Infrastructure](#infrastructure) - [GatewayRouter](#gatewayrouter) - [RoutingStrategies](#routing-strategies) - [CircuitBreaker](#circuitbreaker) - [Models](#models) - [RoutingContext](#routingcontext) - [ServerInfo](#serverinfo) - [GatewayConfig](#gatewayconfig) - [ASP.NET Core Integration](#aspnet-core-integration) - [Service Extensions](#service-extensions) - [Endpoint Mapping](#endpoint-mapping) - [Monitoring](#monitoring) --- ## Core Abstractions ### IGatewayRouter **Namespace:** `Svrnty.MCP.Gateway.Core.Abstractions` Interface defining the gateway routing contract. #### Methods ##### RouteRequestAsync ```csharp Task RouteRequestAsync( RoutingContext context, CancellationToken cancellationToken = default) ``` Routes an MCP request to an appropriate backend server. **Parameters:** - `context` (RoutingContext): Request routing context - `cancellationToken` (CancellationToken): Cancellation support **Returns:** `Task` - The result from the backend server **Throws:** - `NoHealthyServersException` - If all servers are unhealthy - `RoutingException` - If routing decision fails - `CircuitBreakerOpenException` - If circuit breaker is open **Example:** ```csharp var context = new RoutingContext { ClientId = "web-client", ToolName = "search_documents", Parameters = new Dictionary { ["query"] = "architecture" } }; var result = await router.RouteRequestAsync(context); ``` ##### GetServerHealthAsync ```csharp Task> GetServerHealthAsync( CancellationToken cancellationToken = default) ``` Gets health status of all registered servers. **Returns:** `Task>` - Health information for each server **Example:** ```csharp var healthStatuses = await router.GetServerHealthAsync(); foreach (var status in healthStatuses) { Console.WriteLine($"{status.ServerName}: {status.Status}"); } ``` --- ### IRoutingStrategy **Namespace:** `Svrnty.MCP.Gateway.Core.Abstractions` Interface for implementing custom routing logic. #### Methods ##### SelectServer ```csharp string SelectServer( RoutingContext context, IEnumerable availableServers) ``` Selects a server to handle the request. **Parameters:** - `context` (RoutingContext): Request context - `availableServers` (IEnumerable): Healthy servers **Returns:** `string` - ID of the selected server **Throws:** - `NoServerAvailableException` - If no servers match criteria **Example Implementation:** ```csharp public class ToolBasedRoutingStrategy : IRoutingStrategy { public string SelectServer( RoutingContext context, IEnumerable servers) { // Route based on tool name pattern if (context.ToolName.StartsWith("search_")) { return servers.First(s => s.Name == "search-server").Id; } // Default: first available return servers.First().Id; } } ``` --- ### ICircuitBreaker **Namespace:** `Svrnty.MCP.Gateway.Core.Abstractions` Interface for circuit breaker pattern implementation. #### Properties ##### State ```csharp CircuitBreakerState State { get; } ``` Current state of the circuit breaker. ```csharp public enum CircuitBreakerState { Closed, // Normal operation Open, // Failing, rejecting requests HalfOpen // Testing if recovered } ``` #### Methods ##### IsOpen ```csharp bool IsOpen(string serverId) ``` Checks if circuit breaker is open for a specific server. **Parameters:** - `serverId` (string): Server identifier **Returns:** `bool` - True if circuit is open (rejecting requests) ##### RecordSuccess ```csharp Task RecordSuccessAsync(string serverId) ``` Records a successful request. ##### RecordFailure ```csharp Task RecordFailureAsync(string serverId, Exception exception) ``` Records a failed request. **Example:** ```csharp if (_circuitBreaker.IsOpen("server-1")) { throw new CircuitBreakerOpenException("server-1"); } try { var result = await CallServerAsync("server-1", request); await _circuitBreaker.RecordSuccessAsync("server-1"); return result; } catch (Exception ex) { await _circuitBreaker.RecordFailureAsync("server-1", ex); throw; } ``` --- ## Infrastructure ### GatewayRouter **Namespace:** `Svrnty.MCP.Gateway.Infrastructure` Default implementation of `IGatewayRouter`. #### Constructor ```csharp public GatewayRouter( IRoutingStrategy routingStrategy, ICircuitBreaker circuitBreaker, IServerRegistry serverRegistry, ILogger logger = null) ``` **Parameters:** - `routingStrategy` (IRoutingStrategy): Strategy for server selection - `circuitBreaker` (ICircuitBreaker): Circuit breaker implementation - `serverRegistry` (IServerRegistry): Registry of backend servers - `logger` (ILogger): Optional logger **Example:** ```csharp var registry = new ServerRegistry(); registry.AddServer(new ServerInfo { Id = "server-1", Name = "CODEX Server", BaseUrl = "http://localhost:5050" }); var router = new GatewayRouter( new RoundRobinStrategy(), new CircuitBreaker(), registry ); ``` --- ### Routing Strategies Built-in routing strategy implementations. #### RoundRobinStrategy Distributes requests evenly across all healthy servers. ```csharp public class RoundRobinStrategy : IRoutingStrategy { public string SelectServer( RoutingContext context, IEnumerable servers) { // Round-robin logic var serverList = servers.ToList(); var index = _counter++ % serverList.Count; return serverList[index].Id; } } ``` **Use Case:** Load balancing across identical servers #### LeastConnectionsStrategy Routes to the server with fewest active connections. ```csharp public class LeastConnectionsStrategy : IRoutingStrategy { public string SelectServer( RoutingContext context, IEnumerable servers) { return servers .OrderBy(s => s.ActiveConnections) .First() .Id; } } ``` **Use Case:** Balancing load when servers have different capacities #### ToolBasedStrategy Routes based on tool name patterns. ```csharp public class ToolBasedStrategy : IRoutingStrategy { private readonly Dictionary _toolToServerMap; public string SelectServer( RoutingContext context, IEnumerable servers) { if (_toolToServerMap.TryGetValue(context.ToolName, out var serverId)) { return serverId; } // Default fallback return servers.First().Id; } } ``` **Use Case:** Route specific tools to specialized servers --- ### CircuitBreaker **Namespace:** `Svrnty.MCP.Gateway.Infrastructure.Resilience` Default circuit breaker implementation. #### Configuration ```csharp public class CircuitBreakerConfig { public int FailureThreshold { get; set; } = 5; public int SuccessThreshold { get; set; } = 2; public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1); } ``` #### Usage ```csharp var config = new CircuitBreakerConfig { FailureThreshold = 5, // Open after 5 failures SuccessThreshold = 2, // Close after 2 successes Timeout = TimeSpan.FromMinutes(1) // Test recovery after 1 min }; var circuitBreaker = new CircuitBreaker(config); ``` --- ## Models ### RoutingContext **Namespace:** `Svrnty.MCP.Gateway.Core.Models` Context information for routing decisions. #### Properties ```csharp public class RoutingContext { public string ClientId { get; set; } public string ToolName { get; set; } public Dictionary Parameters { get; set; } public Dictionary Headers { get; set; } public DateTime Timestamp { get; set; } } ``` #### Example ```csharp var context = new RoutingContext { ClientId = "web-client-123", ToolName = "search_documents", Parameters = new Dictionary { ["query"] = "test" }, Headers = new Dictionary { ["X-Client-Version"] = "1.0.0" }, Timestamp = DateTime.UtcNow }; ``` --- ### ServerInfo **Namespace:** `Svrnty.MCP.Gateway.Core.Models` Information about a registered backend server. #### Properties ```csharp public class ServerInfo { public string Id { get; set; } public string Name { get; set; } public string BaseUrl { get; set; } public bool IsHealthy { get; set; } public int ActiveConnections { get; set; } public DateTime LastHealthCheck { get; set; } public TimeSpan AverageResponseTime { get; set; } } ``` #### Example ```csharp var serverInfo = new ServerInfo { Id = "codex-server-1", Name = "CODEX MCP Server 1", BaseUrl = "http://localhost:5050", IsHealthy = true, ActiveConnections = 3, LastHealthCheck = DateTime.UtcNow, AverageResponseTime = TimeSpan.FromMilliseconds(45) }; ``` --- ### GatewayConfig **Namespace:** `Svrnty.MCP.Gateway.Core.Models` Gateway configuration. #### Properties ```csharp public class GatewayConfig { public string Name { get; set; } public string Version { get; set; } public string ListenAddress { get; set; } public List Servers { get; set; } public RoutingConfig Routing { get; set; } public SecurityConfig Security { get; set; } } public class ServerConfig { public string Id { get; set; } public string Name { get; set; } public string BaseUrl { get; set; } public bool Enabled { get; set; } } public class RoutingConfig { public string Strategy { get; set; } // "RoundRobin", "LeastConnections", "ToolBased" public TimeSpan HealthCheckInterval { get; set; } } ``` --- ## ASP.NET Core Integration ### Service Extensions **Namespace:** `Svrnty.MCP.Gateway.AspNetCore` #### AddMcpGateway ```csharp public static IServiceCollection AddMcpGateway( this IServiceCollection services, IConfiguration configuration) ``` Registers gateway services and dependencies. **Configuration:** ```json { "Mcp": { "Gateway": { "Name": "MyGateway", "Version": "1.0.0", "ListenAddress": "http://localhost:8080" }, "Servers": [ ... ], "Routing": { "Strategy": "RoundRobin", "HealthCheckInterval": "00:00:30" } } } ``` **Example:** ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddMcpGateway( builder.Configuration.GetSection("Mcp") ); builder.Services.AddHealthChecks() .AddCheck("mcp-servers"); var app = builder.Build(); app.MapMcpGateway(); app.MapHealthChecks("/health"); app.Run(); ``` --- ### Endpoint Mapping #### MapMcpGateway ```csharp public static IEndpointRouteBuilder MapMcpGateway( this IEndpointRouteBuilder endpoints) ``` Maps gateway HTTP endpoints. **Endpoints:** - `POST /mcp/invoke` - Route MCP requests - `GET /health` - Gateway health check - `GET /servers` - List backend servers - `GET /servers/{id}/health` - Server-specific health **Example:** ```csharp var app = builder.Build(); app.MapMcpGateway(); ``` --- ## Monitoring ### Metrics OpenTelemetry metrics exposed by the gateway: ```csharp public class GatewayMetrics { public Counter TotalRequests { get; } public Histogram RequestDuration { get; } public Counter TotalErrors { get; } public Gauge ServerHealth { get; } public Gauge CircuitBreakerState { get; } } ``` **Metric Names:** - `mcp_gateway_requests_total` - `mcp_gateway_request_duration_ms` - `mcp_gateway_errors_total` - `mcp_gateway_server_health` - `mcp_gateway_circuit_breaker_state` **Example Query (Prometheus):** ```promql # Average request latency rate(mcp_gateway_request_duration_ms_sum[5m]) / rate(mcp_gateway_request_duration_ms_count[5m]) # Error rate rate(mcp_gateway_errors_total[5m]) / rate(mcp_gateway_requests_total[5m]) ``` --- ### Health Checks #### Gateway Health Endpoint ```bash curl http://localhost:8080/health ``` **Response:** ```json { "status": "Healthy", "totalServers": 3, "healthyServers": 3, "degradedServers": 0, "unhealthyServers": 0, "servers": [ { "id": "codex-server-1", "name": "CODEX Server 1", "status": "Healthy", "lastCheck": "2025-10-19T12:00:00Z", "responseTime": "12ms" } ] } ``` #### Custom Health Check ```csharp public class McpServerHealthCheck : IHealthCheck { private readonly IGatewayRouter _router; public async Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default) { var serverHealth = await _router.GetServerHealthAsync(cancellationToken); var unhealthyServers = serverHealth .Count(s => s.Status != ServerHealthStatus.Healthy); if (unhealthyServers == 0) { return HealthCheckResult.Healthy("All servers healthy"); } if (unhealthyServers == serverHealth.Count()) { return HealthCheckResult.Unhealthy("All servers unhealthy"); } return HealthCheckResult.Degraded( $"{unhealthyServers} of {serverHealth.Count()} servers unhealthy" ); } } ``` --- ## Complete Example ### Creating a Gateway ```csharp using Svrnty.MCP.Gateway.AspNetCore; using Svrnty.MCP.Gateway.Core; var builder = WebApplication.CreateBuilder(args); // Add gateway services builder.Services.AddMcpGateway( builder.Configuration.GetSection("Mcp") ); // Add custom routing strategy builder.Services.AddSingleton(); // Add health checks builder.Services.AddHealthChecks() .AddCheck("mcp-servers"); var app = builder.Build(); // Map gateway endpoints app.MapMcpGateway(); app.MapHealthChecks("/health"); // Start gateway await app.RunAsync(); ``` ### Custom Routing Strategy ```csharp public class CustomRoutingStrategy : IRoutingStrategy { public string SelectServer( RoutingContext context, IEnumerable servers) { // Route admin clients to dedicated server if (context.ClientId.StartsWith("admin-")) { return servers.First(s => s.Name.Contains("admin")).Id; } // Route search tools to search-optimized server if (context.ToolName.StartsWith("search_")) { return servers.First(s => s.Name.Contains("search")).Id; } // Default: least connections return servers .OrderBy(s => s.ActiveConnections) .First() .Id; } } ``` ### Using the Gateway (Client Side) ```bash # Route request through gateway curl -X POST http://localhost:8080/mcp/invoke \ -H "Content-Type: application/json" \ -H "X-Client-Id: web-client" \ -d '{ "jsonrpc": "2.0", "id": "1", "method": "tools/call", "params": { "name": "search_documents", "arguments": { "query": "architecture" } } }' ``` --- ## See Also - [Gateway Architecture](../architecture.md) - [Module Design](../module-design.md) - [Routing Strategies](../routing-strategies.md) - [Security Guide](../security.md) - [AGENT-PRIMER.md](../../AGENT-PRIMER.md) --- **Document Type:** API Reference **Version:** 1.0.0 **Last Updated:** 2025-10-19 **Maintained By:** Svrnty Development Team