svrnty-mcp-client/README.md
Svrnty 97880406dc 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

549 lines
15 KiB
Markdown

# Svrnty.MCP.Client
**A modular, scalable, secure .NET library for consuming Model Context Protocol (MCP) servers**
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![.NET 8.0](https://img.shields.io/badge/.NET-8.0-512BD4)](https://dotnet.microsoft.com/download/dotnet/8.0)
[![Architecture: Clean](https://img.shields.io/badge/Architecture-Clean-green)](docs/architecture.md)
---
## What is Svrnty.MCP.Client?
Svrnty.MCP.Client is a **standalone, reusable .NET library** that enables any .NET application to act as an MCP client, allowing your application to discover and call tools exposed by MCP servers.
**Model Context Protocol (MCP)** is an industry-standard protocol backed by Anthropic that defines how AI agents communicate with external tools and data sources. Think of it as a universal adapter that lets your application safely access capabilities from remote MCP servers.
### Key Features
- **Modular & Reusable**: Copy to any .NET project, configure, and go
- **Clean Architecture**: Core abstractions, infrastructure implementation, ASP.NET Core integration
- **Security-First**: Connection validation, timeout handling, error recovery
- **Transport Flexibility**: HTTP (primary for production) and stdio (legacy for local tools)
- **AI-Automated Setup**: AGENT-PRIMER.md guides AI assistants to configure your integration automatically
- **TDD Foundation**: Built with test-driven development, comprehensive test coverage
- **Production-Ready**: Observability, error handling, connection pooling, retry logic
---
## Why Svrnty.MCP.Client?
**Problem**: Your .NET application needs to access tools and capabilities exposed by remote MCP servers (search, data processing, API access) but has no standardized way to connect.
**Solution**: Svrnty.MCP.Client transforms your application into an MCP client, allowing you to discover, validate, and call tools from any MCP server with proper error handling and connection management.
**Use Cases**:
- Connect your app to Claude Desktop's exposed tools
- Call tools from remote knowledge bases or search engines
- Integrate with third-party MCP servers for document processing
- Build AI-powered workflows that consume multiple MCP services
- Access enterprise MCP servers for data analysis and reporting
---
## Quick Start
### Prerequisites
- .NET 8.0 SDK or higher
- Your existing .NET application (Web API, Console, Worker Service, etc.)
- Access to one or more MCP servers (local or remote)
### Option 1: AI-Automated Setup (Recommended)
If you have access to Claude or another AI assistant:
1. Copy this entire folder to your project directory
2. Open your AI assistant and say: "Read AGENT-PRIMER.md and set up Svrnty.MCP.Client for my project"
3. The AI will analyze your system, generate configuration, and create sample client code automatically
### Option 2: Manual Setup
#### Step 1: Add Package Reference
```bash
# Via project reference (development)
dotnet add reference /path/to/Svrnty.MCP.Client/src/Svrnty.MCP.Client.AspNetCore/Svrnty.MCP.Client.AspNetCore.csproj
# OR via NuGet (when published)
# dotnet add package Svrnty.MCP.Client.AspNetCore
```
#### Step 2: Configure appsettings.json
Add MCP client configuration:
```json
{
"Mcp": {
"Client": {
"Name": "MyAppMcpClient",
"Version": "1.0.0",
"Description": "MCP client for MyApp"
},
"Servers": [
{
"Name": "codex-server",
"Transport": {
"Type": "Http",
"BaseUrl": "http://localhost:5050"
},
"Timeout": "00:00:30",
"Enabled": true
},
{
"Name": "remote-mcp-server",
"Transport": {
"Type": "Http",
"BaseUrl": "https://api.example.com/mcp"
},
"Timeout": "00:00:60",
"Enabled": true
}
],
"Connection": {
"MaxRetries": 3,
"RetryDelayMs": 1000,
"EnableConnectionPooling": true
}
}
}
```
#### Step 3: Update Program.cs
```csharp
using Svrnty.MCP.Client.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
// Add MCP client
builder.Services.AddMcpClient(builder.Configuration.GetSection("Mcp"));
var app = builder.Build();
app.Run();
```
#### Step 4: Use the Client to Call Tools
```csharp
using Svrnty.MCP.Client.Core.Abstractions;
public class MyService
{
private readonly IMcpClient _mcpClient;
public MyService(IMcpClient mcpClient)
{
_mcpClient = mcpClient;
}
public async Task<string> SearchCodexAsync(string query)
{
// List available tools from the codex-server
var tools = await _mcpClient.ListToolsAsync("codex-server");
// Call the search_codex tool
var result = await _mcpClient.CallToolAsync(
serverName: "codex-server",
toolName: "search_codex",
arguments: new Dictionary<string, object>
{
["query"] = query,
["maxResults"] = 10
}
);
return result.Content;
}
}
```
#### Step 5: Run and Test
```bash
# Ensure MCP servers are running
# Terminal 1: Start CODEX MCP Server
dotnet run --project /path/to/CodexMcpServer
# Server listens on http://localhost:5050
# Terminal 2: Run your client application
dotnet run
# The client will automatically connect to configured MCP servers via HTTP
# and be ready to call their tools
```
**Legacy Stdio Transport** (for local process-based tools):
```json
{
"Servers": [
{
"Name": "local-tool",
"Transport": {
"Type": "Stdio",
"Command": "dotnet",
"Args": ["run", "--project", "/path/to/tool", "--", "--stdio"]
},
"Enabled": true
}
]
}
```
Note: HTTP transport is recommended for production with remote servers, load balancing, and monitoring.
---
## Architecture
Svrnty.MCP.Client follows **Clean Architecture** principles:
```
┌─────────────────────────────────────────────────┐
│ Svrnty.MCP.Client.Cli (Executable) │
│ ┌───────────────────────────────────────────┐ │
│ │ Svrnty.MCP.Client.AspNetCore (DI) │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ Svrnty.MCP.Client.Infrastructure│ │ │
│ │ │ ┌───────────────────────────────┐ │ │ │
│ │ │ │ Svrnty.MCP.Client.Core │ │ │ │
│ │ │ │ - IMcpClient │ │ │ │
│ │ │ │ - IMcpServerConnection │ │ │ │
│ │ │ │ - IConnectionPool │ │ │ │
│ │ │ │ - Models (no dependencies) │ │ │ │
│ │ │ └───────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
```
### Projects
| Project | Purpose | Dependencies |
|---------|---------|--------------|
| **Svrnty.MCP.Client.Core** | Abstractions, interfaces, models | None |
| **Svrnty.MCP.Client.Infrastructure** | MCP client implementation, transports, connection management | Core, System.Text.Json |
| **Svrnty.MCP.Client.AspNetCore** | ASP.NET Core integration, DI extensions | Core, Infrastructure, ASP.NET Core |
| **Svrnty.MCP.Client.Cli** | Standalone CLI executable | All above |
See [Architecture Documentation](docs/architecture.md) for detailed design.
---
## Examples
### 1. CodexMcpClient (Knowledge Search)
Sample client application that connects to CODEX MCP Server:
```
samples/CodexMcpClient/
├── Services/
│ ├── CodexSearchService.cs # Search documents
│ ├── DocumentService.cs # Retrieve documents
│ └── TagService.cs # List and filter tags
├── Program.cs
└── appsettings.json
```
**Running the sample**:
```bash
# Terminal 1: Start CODEX MCP Server
cd /path/to/Svrnty.MCP.Server/samples/CodexMcpServer
dotnet run
# Server listens on http://localhost:5050
# Terminal 2: Run client commands
cd /path/to/Svrnty.MCP.Client/samples/CodexMcpClient
dotnet run -- search "architecture patterns"
dotnet run -- get-document <id>
dotnet run -- list-tags
# Client connects to server via HTTP (configured in appsettings.json)
```
### 2. Multi-Server Client
Connect to multiple MCP servers simultaneously:
```csharp
public class MultiServerService
{
private readonly IMcpClient _mcpClient;
public async Task<CombinedResults> SearchAllServersAsync(string query)
{
// Get list of connected servers
var servers = await _mcpClient.GetConnectedServersAsync();
var results = new CombinedResults();
foreach (var server in servers)
{
// List tools available on this server
var tools = await _mcpClient.ListToolsAsync(server.Name);
// Find search tool (if exists)
var searchTool = tools.FirstOrDefault(t => t.Name.Contains("search"));
if (searchTool != null)
{
var result = await _mcpClient.CallToolAsync(
server.Name,
searchTool.Name,
new Dictionary<string, object> { ["query"] = query }
);
results.Add(server.Name, result);
}
}
return results;
}
}
```
### 3. Error Handling and Retry
```csharp
public class ResilientMcpService
{
private readonly IMcpClient _mcpClient;
private readonly ILogger<ResilientMcpService> _logger;
public async Task<McpToolResult> CallWithRetryAsync(
string serverName,
string toolName,
Dictionary<string, object> arguments,
int maxRetries = 3)
{
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
return await _mcpClient.CallToolAsync(serverName, toolName, arguments);
}
catch (McpConnectionException ex) when (attempt < maxRetries - 1)
{
_logger.LogWarning(
"MCP call failed (attempt {Attempt}/{MaxRetries}): {Error}",
attempt + 1, maxRetries, ex.Message
);
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
}
throw new Exception($"Failed after {maxRetries} attempts");
}
}
```
---
## Connection Management
### Connection Pooling
Svrnty.MCP.Client includes connection pooling for efficient resource usage:
```json
{
"Mcp": {
"Connection": {
"EnableConnectionPooling": true,
"MaxConnectionsPerServer": 5,
"ConnectionIdleTimeout": "00:05:00",
"PoolEvictionInterval": "00:01:00"
}
}
}
```
### Timeout Configuration
Configure timeouts per server:
```json
{
"Servers": [
{
"Name": "fast-server",
"Timeout": "00:00:10"
},
{
"Name": "slow-batch-server",
"Timeout": "00:05:00"
}
]
}
```
### Health Checks
Monitor server connections:
```csharp
public class ServerHealthService
{
private readonly IMcpClient _mcpClient;
public async Task<Dictionary<string, bool>> CheckServerHealthAsync()
{
var servers = await _mcpClient.GetConnectedServersAsync();
var health = new Dictionary<string, bool>();
foreach (var server in servers)
{
try
{
await _mcpClient.PingAsync(server.Name);
health[server.Name] = true;
}
catch
{
health[server.Name] = false;
}
}
return health;
}
}
```
---
## Testing
### Integration Tests
```bash
# Run all tests
dotnet test
# Run specific test project
dotnet test tests/Svrnty.MCP.Client.Tests/
# Run with coverage
dotnet test /p:CollectCoverage=true
```
### Mock MCP Server
The library includes a mock server for testing:
```csharp
[Fact]
public async Task Client_CanCallMockServerTool()
{
// Arrange
var mockServer = new MockMcpServer()
.WithTool("test_tool", async (args) =>
McpToolResult.Success($"Received: {args["input"]}"));
var client = new McpClient();
await client.ConnectToServerAsync(mockServer);
// Act
var result = await client.CallToolAsync(
"mock-server",
"test_tool",
new Dictionary<string, object> { ["input"] = "test" }
);
// Assert
Assert.True(result.IsSuccess);
Assert.Equal("Received: test", result.Content);
}
```
### Test Coverage
Svrnty.MCP.Client maintains **88.52% line coverage** and **75.58% branch coverage** with **60 tests** passing (100%).
**Coverage Breakdown:**
- **Lines**: 88.52% (excellent)
- **Branches**: 75.58% (excellent)
- **Test Projects**: 1
- Svrnty.MCP.Client.Core.Tests: 60 tests
- HTTP client connection tests: 20 tests
- Configuration validation tests
- Error handling and retry logic
**Analysis:**
- **Excellent coverage** - exceeds 85% industry threshold
- All critical paths tested
- Error handling well-covered
- Configuration scenarios comprehensive
**Coverage Reports:**
```bash
# Generate coverage report
dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults
# View detailed coverage
# See: /home/svrnty/codex/COVERAGE-SUMMARY.md for complete analysis
```
**Status**: ✅ Excellent - Production-ready coverage meets all industry standards
---
## Documentation
| Document | Description |
|----------|-------------|
| [**API Reference**](docs/api/) | **Complete API documentation (IMcpClient, Models, Configuration)** |
| [Module Design](docs/module-design.md) | Architecture and design decisions |
| [Implementation Plan](docs/implementation-plan.md) | Development roadmap |
| [AGENT-PRIMER.md](AGENT-PRIMER.md) | AI-assisted setup guide |
| [HTTPS Setup Guide](docs/deployment/https-setup.md) | Production TLS/HTTPS configuration |
---
## Related Modules
Svrnty.MCP is a family of three complementary modules:
- **[Svrnty.MCP.Server](../Svrnty.MCP.Server/)** - Server library (expose tools TO AI agents)
- **[Svrnty.MCP.Client](../Svrnty.MCP.Client/)** - Client library (call tools FROM servers) ← You are here
- **[Svrnty.MCP.Gateway](../Svrnty.MCP.Gateway/)** - Gateway/proxy (route between clients and servers)
All three modules share:
- Same Clean Architecture pattern
- Same documentation structure
- Same security principles
- Compatible .NET 8 SDKs
---
## Contributing
We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for:
- Development setup
- Code standards
- Testing requirements
- Pull request process
---
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
---
## Support
- **Issues**: [GitHub Issues](https://github.com/svrnty/svrnty-mcp/issues)
- **Email**: info@svrnty.io
- **Documentation**: [docs/](docs/)
---
**Built with love by Svrnty**
Creating sovereign tools to democratize technology for humanity.