svrnty-mcp-client/docs/api
Svrnty d936ad7856 docs: comprehensive AI coding assistant research and MCP-first implementation plan
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>
2025-10-22 21:00:34 -04:00
..
README.md docs: comprehensive AI coding assistant research and MCP-first implementation plan 2025-10-22 21:00:34 -04:00

OpenHarbor.MCP.Client - API Reference

Version: 1.0.0 Last Updated: 2025-10-19 Status: Production-Ready


Table of Contents


Core Abstractions

IMcpClient

Namespace: OpenHarbor.MCP.Client.Core.Abstractions

Interface defining the MCP client contract for consuming MCP servers.

Methods

ConnectToServerAsync
Task ConnectToServerAsync(
    string serverName,
    ServerConnectionConfig config,
    CancellationToken cancellationToken = default)

Establishes a connection to an MCP server.

Parameters:

  • serverName (string): Unique identifier for this server
  • config (ServerConnectionConfig): Connection configuration
  • cancellationToken (CancellationToken): Optional cancellation token

Throws:

  • ConnectionException - If connection fails
  • ArgumentException - If server name already exists

Example:

await client.ConnectToServerAsync(
    "codex-server",
    new ServerConnectionConfig
    {
        Transport = TransportType.Http,
        BaseUrl = "http://localhost:5050",
        Timeout = TimeSpan.FromSeconds(30)
    }
);
ListToolsAsync
Task<IEnumerable<McpToolInfo>> ListToolsAsync(
    string serverName,
    CancellationToken cancellationToken = default)

Lists all tools available on a connected server.

Parameters:

  • serverName (string): Name of the server to query
  • cancellationToken (CancellationToken): Optional cancellation token

Returns: Task<IEnumerable<McpToolInfo>> - Collection of available tools

Throws:

  • ServerNotFoundException - If server not connected
  • ConnectionException - If connection lost

Example:

var tools = await client.ListToolsAsync("codex-server");
foreach (var tool in tools)
{
    Console.WriteLine($"{tool.Name}: {tool.Description}");
}
CallToolAsync
Task<McpToolResult> CallToolAsync(
    string serverName,
    string toolName,
    Dictionary<string, object> arguments,
    CancellationToken cancellationToken = default)

Calls a tool on a connected server.

Parameters:

  • serverName (string): Name of the server hosting the tool
  • toolName (string): Name of the tool to execute
  • arguments (Dictionary<string, object>): Tool input parameters
  • cancellationToken (CancellationToken): Optional cancellation token

Returns: Task<McpToolResult> - The result of the tool execution

Throws:

  • ServerNotFoundException - If server not connected
  • ToolNotFoundException - If tool doesn't exist
  • InvalidArgumentsException - If arguments don't match schema
  • ConnectionException - If connection lost

Example:

var result = await client.CallToolAsync(
    serverName: "codex-server",
    toolName: "search_documents",
    arguments: new Dictionary<string, object>
    {
        ["query"] = "architecture patterns",
        ["maxResults"] = 10
    }
);

if (result.IsSuccess)
{
    Console.WriteLine(result.Content);
}
GetConnectedServersAsync
Task<IEnumerable<ServerInfo>> GetConnectedServersAsync(
    CancellationToken cancellationToken = default)

Gets a list of all connected servers.

Returns: Task<IEnumerable<ServerInfo>> - Connected servers with status

Example:

var servers = await client.GetConnectedServersAsync();
foreach (var server in servers)
{
    Console.WriteLine($"{server.Name}: {(server.IsHealthy ? "Healthy" : "Unhealthy")}");
}
DisconnectFromServerAsync
Task DisconnectFromServerAsync(
    string serverName,
    CancellationToken cancellationToken = default)

Disconnects from a specific server.

Parameters:

  • serverName (string): Name of the server to disconnect from
  • cancellationToken (CancellationToken): Optional cancellation token

Example:

await client.DisconnectFromServerAsync("codex-server");

IMcpServerConnection

Namespace: OpenHarbor.MCP.Client.Core.Abstractions

Interface representing a connection to a single MCP server.

Properties

ServerName
string ServerName { get; }

Unique identifier for this server connection.

IsConnected
bool IsConnected { get; }

Indicates whether the connection is active.

Config
ServerConnectionConfig Config { get; }

Configuration used for this connection.

Methods

SendRequestAsync
Task<JsonRpcResponse> SendRequestAsync(
    JsonRpcRequest request,
    CancellationToken cancellationToken = default)

Sends a JSON-RPC request to the server.

Parameters:

  • request (JsonRpcRequest): The request to send
  • cancellationToken (CancellationToken): Cancellation support

Returns: Task<JsonRpcResponse> - The server response

Example:

var request = new JsonRpcRequest
{
    JsonRpc = "2.0",
    Id = "1",
    Method = "tools/list"
};

var response = await connection.SendRequestAsync(request);

Infrastructure

McpClient

Namespace: OpenHarbor.MCP.Client.Infrastructure

Default implementation of IMcpClient.

Constructor

public McpClient(ILogger<McpClient> logger = null)

Parameters:

  • logger (ILogger): Optional logger instance

Example:

var client = new McpClient();

// With logging
var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<McpClient>();
var clientWithLogging = new McpClient(logger);

Configuration

Connection Pooling:

var client = new McpClient();
client.EnableConnectionPooling = true;
client.MaxConnectionsPerServer = 5;
client.ConnectionIdleTimeout = TimeSpan.FromMinutes(5);

Retry Policy:

client.MaxRetries = 3;
client.RetryDelay = TimeSpan.FromSeconds(1);
client.RetryBackoff = RetryBackoffStrategy.Exponential;

HttpServerConnection

Namespace: OpenHarbor.MCP.Client.Infrastructure.Transports

HTTP-based implementation of IMcpServerConnection.

Constructor

public HttpServerConnection(
    string serverName,
    HttpServerConnectionConfig config,
    HttpClient httpClient = null)

Parameters:

  • serverName (string): Server identifier
  • config (HttpServerConnectionConfig): HTTP-specific configuration
  • httpClient (HttpClient): Optional HttpClient (for DI)

Example:

var config = new HttpServerConnectionConfig
{
    ServerUrl = "http://localhost:5050",
    Timeout = TimeSpan.FromSeconds(30),
    ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY")
};

var connection = new HttpServerConnection("codex-server", config);
await connection.ConnectAsync();

Configuration Options

public class HttpServerConnectionConfig : ServerConnectionConfig
{
    public string ServerUrl { get; set; }
    public string ApiKey { get; set; }
    public Dictionary<string, string> Headers { get; set; }
    public TimeSpan Timeout { get; set; }
    public bool ValidateSslCertificate { get; set; }
}

Models

ServerConnectionConfig

Namespace: OpenHarbor.MCP.Client.Core.Models

Base configuration for server connections.

Properties

public class ServerConnectionConfig
{
    public TransportType Transport { get; set; }
    public TimeSpan Timeout { get; set; }
    public bool Enabled { get; set; }
}

public enum TransportType
{
    Http,
    Stdio
}

Example

var config = new ServerConnectionConfig
{
    Transport = TransportType.Http,
    Timeout = TimeSpan.FromSeconds(30),
    Enabled = true
};

McpToolInfo

Namespace: OpenHarbor.MCP.Client.Core.Models

Metadata about an available tool.

Properties

public class McpToolInfo
{
    public string Name { get; set; }
    public string Description { get; set; }
    public McpToolSchema InputSchema { get; set; }
}

Example

var toolInfo = new McpToolInfo
{
    Name = "search_documents",
    Description = "Search documents by query",
    InputSchema = new McpToolSchema { ... }
};

McpToolResult

Namespace: OpenHarbor.MCP.Client.Core.Models

Result of a tool execution.

Properties

public class McpToolResult
{
    public bool IsSuccess { get; set; }
    public string Content { get; set; }
    public string ErrorMessage { get; set; }
    public int? ErrorCode { get; set; }
}

Usage

var result = await client.CallToolAsync(...);

if (result.IsSuccess)
{
    Console.WriteLine($"Success: {result.Content}");
}
else
{
    Console.WriteLine($"Error {result.ErrorCode}: {result.ErrorMessage}");
}

ASP.NET Core Integration

Service Extensions

Namespace: OpenHarbor.MCP.Client.AspNetCore

AddMcpClient

public static IServiceCollection AddMcpClient(
    this IServiceCollection services,
    IConfiguration configuration)

Registers MCP client and dependencies from configuration.

Configuration Format:

{
  "Mcp": {
    "Client": {
      "Name": "MyAppClient",
      "Version": "1.0.0"
    },
    "Servers": [
      {
        "Name": "codex-server",
        "Transport": {
          "Type": "Http",
          "BaseUrl": "http://localhost:5050"
        },
        "Timeout": "00:00:30",
        "Enabled": true
      }
    ]
  }
}

Example:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMcpClient(
    builder.Configuration.GetSection("Mcp")
);

var app = builder.Build();

Dependency Injection

public class MyService
{
    private readonly IMcpClient _mcpClient;

    public MyService(IMcpClient mcpClient)
    {
        _mcpClient = mcpClient;
    }

    public async Task<string> SearchAsync(string query)
    {
        var result = await _mcpClient.CallToolAsync(
            "codex-server",
            "search_documents",
            new Dictionary<string, object> { ["query"] = query }
        );

        return result.Content;
    }
}

Configuration

appsettings.json Structure

{
  "Mcp": {
    "Client": {
      "Name": "MyApplication",
      "Version": "1.0.0",
      "Description": "My MCP client application"
    },
    "Servers": [
      {
        "Name": "codex-server",
        "Transport": {
          "Type": "Http",
          "BaseUrl": "http://localhost:5050"
        },
        "Timeout": "00:00:30",
        "Enabled": true
      }
    ],
    "Connection": {
      "MaxRetries": 3,
      "RetryDelayMs": 1000,
      "EnableConnectionPooling": true,
      "MaxConnectionsPerServer": 5,
      "ConnectionIdleTimeout": "00:05:00"
    }
  }
}

Environment Variables

Override configuration with environment variables:

export MCP__SERVERS__0__TRANSPORT__BASEURL="https://production.example.com"
export MCP__CONNECTION__MAXRETRIES="5"

Error Handling

Exception Hierarchy

McpException (base)
├── ConnectionException
│   ├── ServerNotFoundException
│   ├── ConnectionTimeoutException
│   └── ServerUnavailableException
├── ToolException
│   ├── ToolNotFoundException
│   └── InvalidArgumentsException
└── TransportException
    ├── HttpTransportException
    └── StdioTransportException

Example Error Handling

try
{
    var result = await client.CallToolAsync(...);
}
catch (ServerNotFoundException ex)
{
    _logger.LogError($"Server not found: {ex.ServerName}");
}
catch (ToolNotFoundException ex)
{
    _logger.LogError($"Tool not found: {ex.ToolName}");
}
catch (ConnectionTimeoutException ex)
{
    _logger.LogWarning($"Timeout connecting to {ex.ServerName}");
    // Retry logic
}
catch (McpException ex)
{
    _logger.LogError($"MCP error: {ex.Message}");
}

Complete Example

Console Application

using OpenHarbor.MCP.Client.Core;
using OpenHarbor.MCP.Client.Infrastructure;

class Program
{
    static async Task Main(string[] args)
    {
        // 1. Create client
        var client = new McpClient();

        // 2. Connect to server
        await client.ConnectToServerAsync(
            "codex-server",
            new ServerConnectionConfig
            {
                Transport = TransportType.Http,
                BaseUrl = "http://localhost:5050",
                Timeout = TimeSpan.FromSeconds(30)
            }
        );

        // 3. List available tools
        var tools = await client.ListToolsAsync("codex-server");
        Console.WriteLine($"Found {tools.Count()} tools:");
        foreach (var tool in tools)
        {
            Console.WriteLine($"  - {tool.Name}: {tool.Description}");
        }

        // 4. Call a tool
        var result = await client.CallToolAsync(
            serverName: "codex-server",
            toolName: "search_documents",
            arguments: new Dictionary<string, object>
            {
                ["query"] = "architecture",
                ["maxResults"] = 5
            }
        );

        // 5. Handle result
        if (result.IsSuccess)
        {
            Console.WriteLine($"Results: {result.Content}");
        }
        else
        {
            Console.WriteLine($"Error: {result.ErrorMessage}");
        }

        // 6. Disconnect
        await client.DisconnectFromServerAsync("codex-server");
    }
}

ASP.NET Core Web API

[ApiController]
[Route("api/[controller]")]
public class SearchController : ControllerBase
{
    private readonly IMcpClient _mcpClient;

    public SearchController(IMcpClient mcpClient)
    {
        _mcpClient = mcpClient;
    }

    [HttpGet]
    public async Task<IActionResult> Search([FromQuery] string query)
    {
        try
        {
            var result = await _mcpClient.CallToolAsync(
                "codex-server",
                "search_documents",
                new Dictionary<string, object> { ["query"] = query }
            );

            if (result.IsSuccess)
            {
                return Ok(result.Content);
            }

            return StatusCode(500, result.ErrorMessage);
        }
        catch (ServerNotFoundException ex)
        {
            return NotFound($"Server not found: {ex.ServerName}");
        }
        catch (Exception ex)
        {
            return StatusCode(500, ex.Message);
        }
    }
}

See Also


Document Type: API Reference Version: 1.0.0 Last Updated: 2025-10-19 Maintained By: Svrnty Development Team