- 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>
736 lines
15 KiB
Markdown
736 lines
15 KiB
Markdown
# Svrnty.MCP.Client - API Reference
|
|
|
|
**Version:** 1.0.0
|
|
**Last Updated:** 2025-10-19
|
|
**Status:** Production-Ready
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
- [Core Abstractions](#core-abstractions)
|
|
- [IMcpClient](#imcpclient)
|
|
- [IMcpServerConnection](#imcpserverconnection)
|
|
- [Infrastructure](#infrastructure)
|
|
- [McpClient](#mcpclient)
|
|
- [HttpServerConnection](#httpserverconnection)
|
|
- [Models](#models)
|
|
- [ServerConnectionConfig](#serverconnectionconfig)
|
|
- [McpToolInfo](#mcptoolinfo)
|
|
- [McpToolResult](#mcptoolresult)
|
|
- [ASP.NET Core Integration](#aspnet-core-integration)
|
|
- [Service Extensions](#service-extensions)
|
|
- [Configuration](#configuration)
|
|
|
|
---
|
|
|
|
## Core Abstractions
|
|
|
|
### IMcpClient
|
|
|
|
**Namespace:** `Svrnty.MCP.Client.Core.Abstractions`
|
|
|
|
Interface defining the MCP client contract for consuming MCP servers.
|
|
|
|
#### Methods
|
|
|
|
##### ConnectToServerAsync
|
|
|
|
```csharp
|
|
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:**
|
|
```csharp
|
|
await client.ConnectToServerAsync(
|
|
"codex-server",
|
|
new ServerConnectionConfig
|
|
{
|
|
Transport = TransportType.Http,
|
|
BaseUrl = "http://localhost:5050",
|
|
Timeout = TimeSpan.FromSeconds(30)
|
|
}
|
|
);
|
|
```
|
|
|
|
##### ListToolsAsync
|
|
|
|
```csharp
|
|
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:**
|
|
```csharp
|
|
var tools = await client.ListToolsAsync("codex-server");
|
|
foreach (var tool in tools)
|
|
{
|
|
Console.WriteLine($"{tool.Name}: {tool.Description}");
|
|
}
|
|
```
|
|
|
|
##### CallToolAsync
|
|
|
|
```csharp
|
|
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:**
|
|
```csharp
|
|
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
|
|
|
|
```csharp
|
|
Task<IEnumerable<ServerInfo>> GetConnectedServersAsync(
|
|
CancellationToken cancellationToken = default)
|
|
```
|
|
|
|
Gets a list of all connected servers.
|
|
|
|
**Returns:** `Task<IEnumerable<ServerInfo>>` - Connected servers with status
|
|
|
|
**Example:**
|
|
```csharp
|
|
var servers = await client.GetConnectedServersAsync();
|
|
foreach (var server in servers)
|
|
{
|
|
Console.WriteLine($"{server.Name}: {(server.IsHealthy ? "Healthy" : "Unhealthy")}");
|
|
}
|
|
```
|
|
|
|
##### DisconnectFromServerAsync
|
|
|
|
```csharp
|
|
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:**
|
|
```csharp
|
|
await client.DisconnectFromServerAsync("codex-server");
|
|
```
|
|
|
|
---
|
|
|
|
### IMcpServerConnection
|
|
|
|
**Namespace:** `Svrnty.MCP.Client.Core.Abstractions`
|
|
|
|
Interface representing a connection to a single MCP server.
|
|
|
|
#### Properties
|
|
|
|
##### ServerName
|
|
|
|
```csharp
|
|
string ServerName { get; }
|
|
```
|
|
|
|
Unique identifier for this server connection.
|
|
|
|
##### IsConnected
|
|
|
|
```csharp
|
|
bool IsConnected { get; }
|
|
```
|
|
|
|
Indicates whether the connection is active.
|
|
|
|
##### Config
|
|
|
|
```csharp
|
|
ServerConnectionConfig Config { get; }
|
|
```
|
|
|
|
Configuration used for this connection.
|
|
|
|
#### Methods
|
|
|
|
##### SendRequestAsync
|
|
|
|
```csharp
|
|
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:**
|
|
```csharp
|
|
var request = new JsonRpcRequest
|
|
{
|
|
JsonRpc = "2.0",
|
|
Id = "1",
|
|
Method = "tools/list"
|
|
};
|
|
|
|
var response = await connection.SendRequestAsync(request);
|
|
```
|
|
|
|
---
|
|
|
|
## Infrastructure
|
|
|
|
### McpClient
|
|
|
|
**Namespace:** `Svrnty.MCP.Client.Infrastructure`
|
|
|
|
Default implementation of `IMcpClient`.
|
|
|
|
#### Constructor
|
|
|
|
```csharp
|
|
public McpClient(ILogger<McpClient> logger = null)
|
|
```
|
|
|
|
**Parameters:**
|
|
- `logger` (ILogger<McpClient>): Optional logger instance
|
|
|
|
**Example:**
|
|
```csharp
|
|
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:**
|
|
```csharp
|
|
var client = new McpClient();
|
|
client.EnableConnectionPooling = true;
|
|
client.MaxConnectionsPerServer = 5;
|
|
client.ConnectionIdleTimeout = TimeSpan.FromMinutes(5);
|
|
```
|
|
|
|
**Retry Policy:**
|
|
```csharp
|
|
client.MaxRetries = 3;
|
|
client.RetryDelay = TimeSpan.FromSeconds(1);
|
|
client.RetryBackoff = RetryBackoffStrategy.Exponential;
|
|
```
|
|
|
|
---
|
|
|
|
### HttpServerConnection
|
|
|
|
**Namespace:** `Svrnty.MCP.Client.Infrastructure.Transports`
|
|
|
|
HTTP-based implementation of `IMcpServerConnection`.
|
|
|
|
#### Constructor
|
|
|
|
```csharp
|
|
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:**
|
|
```csharp
|
|
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
|
|
|
|
```csharp
|
|
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:** `Svrnty.MCP.Client.Core.Models`
|
|
|
|
Base configuration for server connections.
|
|
|
|
#### Properties
|
|
|
|
```csharp
|
|
public class ServerConnectionConfig
|
|
{
|
|
public TransportType Transport { get; set; }
|
|
public TimeSpan Timeout { get; set; }
|
|
public bool Enabled { get; set; }
|
|
}
|
|
|
|
public enum TransportType
|
|
{
|
|
Http,
|
|
Stdio
|
|
}
|
|
```
|
|
|
|
#### Example
|
|
|
|
```csharp
|
|
var config = new ServerConnectionConfig
|
|
{
|
|
Transport = TransportType.Http,
|
|
Timeout = TimeSpan.FromSeconds(30),
|
|
Enabled = true
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
### McpToolInfo
|
|
|
|
**Namespace:** `Svrnty.MCP.Client.Core.Models`
|
|
|
|
Metadata about an available tool.
|
|
|
|
#### Properties
|
|
|
|
```csharp
|
|
public class McpToolInfo
|
|
{
|
|
public string Name { get; set; }
|
|
public string Description { get; set; }
|
|
public McpToolSchema InputSchema { get; set; }
|
|
}
|
|
```
|
|
|
|
#### Example
|
|
|
|
```csharp
|
|
var toolInfo = new McpToolInfo
|
|
{
|
|
Name = "search_documents",
|
|
Description = "Search documents by query",
|
|
InputSchema = new McpToolSchema { ... }
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
### McpToolResult
|
|
|
|
**Namespace:** `Svrnty.MCP.Client.Core.Models`
|
|
|
|
Result of a tool execution.
|
|
|
|
#### Properties
|
|
|
|
```csharp
|
|
public class McpToolResult
|
|
{
|
|
public bool IsSuccess { get; set; }
|
|
public string Content { get; set; }
|
|
public string ErrorMessage { get; set; }
|
|
public int? ErrorCode { get; set; }
|
|
}
|
|
```
|
|
|
|
#### Usage
|
|
|
|
```csharp
|
|
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:** `Svrnty.MCP.Client.AspNetCore`
|
|
|
|
#### AddMcpClient
|
|
|
|
```csharp
|
|
public static IServiceCollection AddMcpClient(
|
|
this IServiceCollection services,
|
|
IConfiguration configuration)
|
|
```
|
|
|
|
Registers MCP client and dependencies from configuration.
|
|
|
|
**Configuration Format:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```csharp
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Services.AddMcpClient(
|
|
builder.Configuration.GetSection("Mcp")
|
|
);
|
|
|
|
var app = builder.Build();
|
|
```
|
|
|
|
#### Dependency Injection
|
|
|
|
```csharp
|
|
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
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```csharp
|
|
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
|
|
|
|
```csharp
|
|
using Svrnty.MCP.Client.Core;
|
|
using Svrnty.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
|
|
|
|
```csharp
|
|
[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
|
|
|
|
- [Client Architecture](../architecture.md)
|
|
- [Module Design](../module-design.md)
|
|
- [Implementation Plan](../implementation-plan.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
|