Research conducted on modern AI coding assistants (Cursor, GitHub Copilot, Cline,
Aider, Windsurf, Replit Agent) to understand architecture patterns, context management,
code editing workflows, and tool use protocols.
Key Decision: Pivoted from building full CLI (40-50h) to validation-driven MCP-first
approach (10-15h). Build 5 core CODEX MCP tools that work with ANY coding assistant,
validate adoption over 2-4 weeks, then decide on full CLI if demand proven.
Files:
- research/ai-systems/modern-coding-assistants-architecture.md (comprehensive research)
- research/ai-systems/codex-coding-assistant-implementation-plan.md (original CLI plan, preserved)
- research/ai-systems/codex-mcp-tools-implementation-plan.md (approved MCP-first plan)
- ideas/registry.json (updated with approved MCP tools proposal)
Architech Validation: APPROVED with pivot to MCP-first approach
Human Decision: Approved (pragmatic validation-driven development)
Next: Begin Phase 1 implementation (10-15 hours, 5 core MCP tools)
🤖 Generated with CODEX Research System
Co-Authored-By: The Archivist <archivist@codex.svrnty.io>
Co-Authored-By: The Architech <architech@codex.svrnty.io>
Co-Authored-By: Mathias Beaulieu-Duncan <mat@svrnty.io>
|
||
|---|---|---|
| .. | ||
| README.md | ||
OpenHarbor.MCP.Gateway - API Reference
Version: 1.0.0 Last Updated: 2025-10-19 Status: Production-Ready
Table of Contents
Core Abstractions
IGatewayRouter
Namespace: OpenHarbor.MCP.Gateway.Core.Abstractions
Interface defining the gateway routing contract.
Methods
RouteRequestAsync
Task<McpToolResult> RouteRequestAsync(
RoutingContext context,
CancellationToken cancellationToken = default)
Routes an MCP request to an appropriate backend server.
Parameters:
context(RoutingContext): Request routing contextcancellationToken(CancellationToken): Cancellation support
Returns: Task<McpToolResult> - The result from the backend server
Throws:
NoHealthyServersException- If all servers are unhealthyRoutingException- If routing decision failsCircuitBreakerOpenException- If circuit breaker is open
Example:
var context = new RoutingContext
{
ClientId = "web-client",
ToolName = "search_documents",
Parameters = new Dictionary<string, object>
{
["query"] = "architecture"
}
};
var result = await router.RouteRequestAsync(context);
GetServerHealthAsync
Task<IEnumerable<ServerHealth>> GetServerHealthAsync(
CancellationToken cancellationToken = default)
Gets health status of all registered servers.
Returns: Task<IEnumerable<ServerHealth>> - Health information for each server
Example:
var healthStatuses = await router.GetServerHealthAsync();
foreach (var status in healthStatuses)
{
Console.WriteLine($"{status.ServerName}: {status.Status}");
}
IRoutingStrategy
Namespace: OpenHarbor.MCP.Gateway.Core.Abstractions
Interface for implementing custom routing logic.
Methods
SelectServer
string SelectServer(
RoutingContext context,
IEnumerable<ServerInfo> availableServers)
Selects a server to handle the request.
Parameters:
context(RoutingContext): Request contextavailableServers(IEnumerable): Healthy servers
Returns: string - ID of the selected server
Throws:
NoServerAvailableException- If no servers match criteria
Example Implementation:
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: OpenHarbor.MCP.Gateway.Core.Abstractions
Interface for circuit breaker pattern implementation.
Properties
State
CircuitBreakerState State { get; }
Current state of the circuit breaker.
public enum CircuitBreakerState
{
Closed, // Normal operation
Open, // Failing, rejecting requests
HalfOpen // Testing if recovered
}
Methods
IsOpen
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
Task RecordSuccessAsync(string serverId)
Records a successful request.
RecordFailure
Task RecordFailureAsync(string serverId, Exception exception)
Records a failed request.
Example:
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: OpenHarbor.MCP.Gateway.Infrastructure
Default implementation of IGatewayRouter.
Constructor
public GatewayRouter(
IRoutingStrategy routingStrategy,
ICircuitBreaker circuitBreaker,
IServerRegistry serverRegistry,
ILogger<GatewayRouter> logger = null)
Parameters:
routingStrategy(IRoutingStrategy): Strategy for server selectioncircuitBreaker(ICircuitBreaker): Circuit breaker implementationserverRegistry(IServerRegistry): Registry of backend serverslogger(ILogger): Optional logger
Example:
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.
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.
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.
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: OpenHarbor.MCP.Gateway.Infrastructure.Resilience
Default circuit breaker implementation.
Configuration
public class CircuitBreakerConfig
{
public int FailureThreshold { get; set; } = 5;
public int SuccessThreshold { get; set; } = 2;
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1);
}
Usage
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: OpenHarbor.MCP.Gateway.Core.Models
Context information for routing decisions.
Properties
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
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: OpenHarbor.MCP.Gateway.Core.Models
Information about a registered backend server.
Properties
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
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: OpenHarbor.MCP.Gateway.Core.Models
Gateway configuration.
Properties
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: OpenHarbor.MCP.Gateway.AspNetCore
AddMcpGateway
public static IServiceCollection AddMcpGateway(
this IServiceCollection services,
IConfiguration configuration)
Registers gateway services and dependencies.
Configuration:
{
"Mcp": {
"Gateway": {
"Name": "MyGateway",
"Version": "1.0.0",
"ListenAddress": "http://localhost:8080"
},
"Servers": [ ... ],
"Routing": {
"Strategy": "RoundRobin",
"HealthCheckInterval": "00:00:30"
}
}
}
Example:
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
public static IEndpointRouteBuilder MapMcpGateway(
this IEndpointRouteBuilder endpoints)
Maps gateway HTTP endpoints.
Endpoints:
POST /mcp/invoke- Route MCP requestsGET /health- Gateway health checkGET /servers- List backend serversGET /servers/{id}/health- Server-specific health
Example:
var app = builder.Build();
app.MapMcpGateway();
Monitoring
Metrics
OpenTelemetry metrics exposed by the gateway:
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_totalmcp_gateway_request_duration_msmcp_gateway_errors_totalmcp_gateway_server_healthmcp_gateway_circuit_breaker_state
Example Query (Prometheus):
# 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
curl http://localhost:8080/health
Response:
{
"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
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
using OpenHarbor.MCP.Gateway.AspNetCore;
using OpenHarbor.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
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)
# 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
Document Type: API Reference Version: 1.0.0 Last Updated: 2025-10-19 Maintained By: Svrnty Development Team