- Renamed all directories: OpenHarbor.MCP.* → Svrnty.MCP.* - Updated all namespaces in 179 C# files - Renamed 20 .csproj files and 3 .sln files - Updated 193 documentation references - Updated 33 references in main CODEX codebase - Updated Codex.sln with new paths - Build verified: 0 errors Preparing for extraction to standalone repositories. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
766 lines
16 KiB
Markdown
766 lines
16 KiB
Markdown
# 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<McpToolResult> 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<McpToolResult>` - 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<string, object>
|
|
{
|
|
["query"] = "architecture"
|
|
}
|
|
};
|
|
|
|
var result = await router.RouteRequestAsync(context);
|
|
```
|
|
|
|
##### GetServerHealthAsync
|
|
|
|
```csharp
|
|
Task<IEnumerable<ServerHealth>> GetServerHealthAsync(
|
|
CancellationToken cancellationToken = default)
|
|
```
|
|
|
|
Gets health status of all registered servers.
|
|
|
|
**Returns:** `Task<IEnumerable<ServerHealth>>` - 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<ServerInfo> availableServers)
|
|
```
|
|
|
|
Selects a server to handle the request.
|
|
|
|
**Parameters:**
|
|
- `context` (RoutingContext): Request context
|
|
- `availableServers` (IEnumerable<ServerInfo>): 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<ServerInfo> 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<GatewayRouter> 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<ServerInfo> 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<ServerInfo> 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<string, string> _toolToServerMap;
|
|
|
|
public string SelectServer(
|
|
RoutingContext context,
|
|
IEnumerable<ServerInfo> 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<string, object> Parameters { get; set; }
|
|
public Dictionary<string, string> Headers { get; set; }
|
|
public DateTime Timestamp { get; set; }
|
|
}
|
|
```
|
|
|
|
#### Example
|
|
|
|
```csharp
|
|
var context = new RoutingContext
|
|
{
|
|
ClientId = "web-client-123",
|
|
ToolName = "search_documents",
|
|
Parameters = new Dictionary<string, object>
|
|
{
|
|
["query"] = "test"
|
|
},
|
|
Headers = new Dictionary<string, string>
|
|
{
|
|
["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<ServerConfig> 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<McpServerHealthCheck>("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<long> TotalRequests { get; }
|
|
public Histogram<double> RequestDuration { get; }
|
|
public Counter<long> TotalErrors { get; }
|
|
public Gauge<int> ServerHealth { get; }
|
|
public Gauge<int> 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<HealthCheckResult> 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<IRoutingStrategy, ToolBasedStrategy>();
|
|
|
|
// Add health checks
|
|
builder.Services.AddHealthChecks()
|
|
.AddCheck<McpServerHealthCheck>("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<ServerInfo> 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
|