svrnty-mcp-gateway/docs/api/README.md
Svrnty 19ef79362e refactor: rename OpenHarbor.MCP to Svrnty.MCP across all libraries
- 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>
2025-10-22 21:04:17 -04:00

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