svrnty-mcp-client/AGENT-PRIMER.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

730 lines
19 KiB
Markdown

# AGENT-PRIMER: Svrnty.MCP.Client Automated Setup
**Purpose**: This document guides AI agents to automatically analyze a target system and configure Svrnty.MCP.Client integration with minimal human intervention.
**Target Audience**: AI assistants (Claude, ChatGPT, etc.) helping developers integrate MCP client capabilities into .NET applications.
---
## Overview
Svrnty.MCP.Client is a **standalone, reusable .NET library** that adds Model Context Protocol (MCP) client capabilities to any .NET application, allowing it to discover and call tools exposed by remote MCP servers. This primer enables AI-automated configuration by walking through system analysis, configuration generation, and validation.
**What you'll automate:**
1. System analysis (detect .NET version, project type, dependencies)
2. Configuration file generation (appsettings.json, client-config.json, Program.cs)
3. Sample MCP client usage code based on detected features
4. Environment setup and validation
---
## Step 1: System Analysis
**Goal**: Understand the target system to generate appropriate configuration.
### Tasks for AI Agent:
#### 1.1 Detect .NET Environment
```bash
# Check .NET SDK version
dotnet --version
# List installed SDKs
dotnet --list-sdks
# List installed runtimes
dotnet --list-runtimes
```
**Required**: .NET 8.0 SDK or higher
**Action if missing**: Provide installation instructions for user's OS
#### 1.2 Analyze Project Structure
```bash
# Find .csproj files
find . -name "*.csproj" -type f
# Examine project type
grep -E "<Project Sdk|<TargetFramework>" *.csproj
```
**Detect**:
- Project type (Web API, Console, Worker Service, etc.)
- Target framework (net8.0, net9.0)
- Existing dependencies
#### 1.3 Identify Integration Points
```bash
# Check for existing HTTP clients
grep -r "HttpClient\|IHttpClientFactory" --include="*.cs"
# Check for dependency injection setup
grep -r "AddScoped\|AddSingleton\|AddTransient" --include="*.cs"
# Check for background services
grep -r "IHostedService\|BackgroundService" --include="*.cs"
```
**Output**: JSON summary of detected features
```json
{
"dotnetVersion": "8.0.100",
"projectType": "AspNetCore.WebApi",
"targetFramework": "net8.0",
"features": {
"hasHttpClient": true,
"hasBackgroundWorkers": true,
"hasAuthentication": true,
"hasLogging": true,
"hasDependencyInjection": true
},
"dependencies": [
"Microsoft.Extensions.Http",
"Microsoft.Extensions.Logging",
"Microsoft.Extensions.DependencyInjection"
]
}
```
---
## Step 2: Generate Configuration
**Goal**: Create configuration files tailored to the detected system.
### 2.1 NuGet Package References
**Add to project's .csproj**:
```xml
<ItemGroup>
<!-- Core MCP Client library -->
<ProjectReference Include="/path/to/Svrnty.MCP.Client.Core/Svrnty.MCP.Client.Core.csproj" />
<!-- Infrastructure (transports, connection management) -->
<ProjectReference Include="/path/to/Svrnty.MCP.Client.Infrastructure/Svrnty.MCP.Client.Infrastructure.csproj" />
<!-- ASP.NET Core integration (if applicable) -->
<ProjectReference Include="/path/to/Svrnty.MCP.Client.AspNetCore/Svrnty.MCP.Client.AspNetCore.csproj" />
</ItemGroup>
```
**Note**: When Svrnty.MCP.Client is published to NuGet, replace with:
```xml
<ItemGroup>
<PackageReference Include="Svrnty.MCP.Client.AspNetCore" Version="1.0.0" />
</ItemGroup>
```
### 2.2 appsettings.json Configuration
**Generate based on detected project**:
```json
{
"Mcp": {
"Client": {
"Name": "MyAppMcpClient",
"Version": "1.0.0",
"Description": "MCP client for MyApp - connects to remote MCP servers"
},
"Servers": [
{
"Name": "local-codex-server",
"Description": "CODEX knowledge base MCP server",
"Transport": {
"Type": "Http",
"Command": "dotnet",
"Args": ["run", "--project", "/path/to/CodexMcpServer/CodexMcpServer.csproj"]
},
"Timeout": "00:00:30",
"Enabled": true
},
{
"Name": "remote-api-server",
"Description": "Remote HTTP-based MCP server",
"Transport": {
"Type": "Http",
"BaseUrl": "https://api.example.com/mcp",
"Headers": {
"Authorization": "Bearer ${MCP_API_KEY}"
}
},
"Timeout": "00:01:00",
"Enabled": false
}
],
"Connection": {
"MaxRetries": 3,
"RetryDelayMs": 1000,
"RetryBackoffMultiplier": 2.0,
"EnableConnectionPooling": true,
"MaxConnectionsPerServer": 5,
"ConnectionIdleTimeout": "00:05:00",
"PoolEvictionInterval": "00:01:00"
},
"Logging": {
"LogToolCalls": true,
"LogConnectionEvents": true,
"LogLevel": "Information"
}
}
}
```
**Configuration explanation**:
- `Client.Name`: Identifier for this MCP client
- `Servers`: List of MCP servers to connect to
- `Transport.Type`: "Stdio" (process) or "Http" (API)
- `Timeout`: How long to wait for responses
- `Enabled`: Whether to auto-connect on startup
- `Connection.MaxRetries`: Retry failed calls automatically
- `Connection.EnableConnectionPooling`: Reuse connections for efficiency
### 2.3 Program.cs Integration
**For ASP.NET Core projects**:
```csharp
using Svrnty.MCP.Client.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
// Add MCP Client
builder.Services.AddMcpClient(builder.Configuration.GetSection("Mcp"));
// Register services that use MCP client
builder.Services.AddScoped<IMyMcpService, MyMcpService>();
var app = builder.Build();
// Optional: Connect to all enabled servers on startup
var mcpClient = app.Services.GetRequiredService<IMcpClient>();
await mcpClient.ConnectToEnabledServersAsync();
app.Run();
```
**For Console applications**:
```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Svrnty.MCP.Client.Infrastructure;
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var services = new ServiceCollection();
// Add MCP Client
services.AddMcpClient(config.GetSection("Mcp"));
var provider = services.BuildServiceProvider();
// Use MCP Client
var mcpClient = provider.GetRequiredService<IMcpClient>();
await mcpClient.ConnectToServerAsync("local-codex-server");
var tools = await mcpClient.ListToolsAsync("local-codex-server");
Console.WriteLine($"Available tools: {string.Join(", ", tools.Select(t => t.Name))}");
```
---
## Step 3: Generate Sample Client Usage
**Goal**: Create service classes that demonstrate MCP client usage based on detected project features.
### 3.1 Basic Search Service
**If project has search/query capabilities**:
```csharp
using Svrnty.MCP.Client.Core.Abstractions;
using Svrnty.MCP.Client.Core.Models;
namespace MyApp.Services;
public interface ICodexSearchService
{
Task<IEnumerable<SearchResult>> SearchAsync(string query, int maxResults = 10);
}
public class CodexSearchService : ICodexSearchService
{
private readonly IMcpClient _mcpClient;
private readonly ILogger<CodexSearchService> _logger;
private const string ServerName = "local-codex-server";
public CodexSearchService(
IMcpClient mcpClient,
ILogger<CodexSearchService> logger)
{
_mcpClient = mcpClient;
_logger = logger;
}
public async Task<IEnumerable<SearchResult>> SearchAsync(
string query,
int maxResults = 10)
{
try
{
var result = await _mcpClient.CallToolAsync(
serverName: ServerName,
toolName: "search_codex",
arguments: new Dictionary<string, object>
{
["query"] = query,
["maxResults"] = maxResults
}
);
if (!result.IsSuccess)
{
_logger.LogWarning(
"MCP tool call failed: {Error}",
result.ErrorMessage
);
return Enumerable.Empty<SearchResult>();
}
return ParseSearchResults(result.Content);
}
catch (McpConnectionException ex)
{
_logger.LogError(ex, "Failed to connect to MCP server");
return Enumerable.Empty<SearchResult>();
}
}
private IEnumerable<SearchResult> ParseSearchResults(string jsonContent)
{
// Parse JSON response and convert to SearchResult models
return JsonSerializer.Deserialize<List<SearchResult>>(jsonContent)
?? Enumerable.Empty<SearchResult>();
}
}
```
### 3.2 Document Retrieval Service
**If project works with documents**:
```csharp
using Svrnty.MCP.Client.Core.Abstractions;
namespace MyApp.Services;
public interface IDocumentService
{
Task<Document?> GetDocumentAsync(string documentId);
Task<IEnumerable<Document>> ListDocumentsAsync(int skip = 0, int take = 20);
}
public class DocumentService : IDocumentService
{
private readonly IMcpClient _mcpClient;
private const string ServerName = "local-codex-server";
public DocumentService(IMcpClient mcpClient)
{
_mcpClient = mcpClient;
}
public async Task<Document?> GetDocumentAsync(string documentId)
{
var result = await _mcpClient.CallToolAsync(
ServerName,
"get_document",
new Dictionary<string, object>
{
["documentId"] = documentId
}
);
if (!result.IsSuccess)
return null;
return JsonSerializer.Deserialize<Document>(result.Content);
}
public async Task<IEnumerable<Document>> ListDocumentsAsync(
int skip = 0,
int take = 20)
{
var result = await _mcpClient.CallToolAsync(
ServerName,
"list_documents",
new Dictionary<string, object>
{
["skip"] = skip,
["take"] = take
}
);
if (!result.IsSuccess)
return Enumerable.Empty<Document>();
return JsonSerializer.Deserialize<List<Document>>(result.Content)
?? Enumerable.Empty<Document>();
}
}
```
### 3.3 Multi-Server Aggregation Service
**For advanced scenarios (multiple MCP servers)**:
```csharp
using Svrnty.MCP.Client.Core.Abstractions;
namespace MyApp.Services;
public interface IAggregationService
{
Task<AggregatedResults> SearchAllServersAsync(string query);
}
public class AggregationService : IAggregationService
{
private readonly IMcpClient _mcpClient;
private readonly ILogger<AggregationService> _logger;
public AggregationService(
IMcpClient mcpClient,
ILogger<AggregationService> logger)
{
_mcpClient = mcpClient;
_logger = logger;
}
public async Task<AggregatedResults> SearchAllServersAsync(string query)
{
var servers = await _mcpClient.GetConnectedServersAsync();
var results = new AggregatedResults();
var tasks = servers.Select(server =>
SearchServerAsync(server.Name, query)
);
var serverResults = await Task.WhenAll(tasks);
foreach (var (serverName, items) in serverResults)
{
if (items.Any())
{
results.Add(serverName, items);
}
}
return results;
}
private async Task<(string ServerName, IEnumerable<SearchResult> Results)>
SearchServerAsync(string serverName, string query)
{
try
{
var tools = await _mcpClient.ListToolsAsync(serverName);
var searchTool = tools.FirstOrDefault(t =>
t.Name.Contains("search", StringComparison.OrdinalIgnoreCase));
if (searchTool == null)
return (serverName, Enumerable.Empty<SearchResult>());
var result = await _mcpClient.CallToolAsync(
serverName,
searchTool.Name,
new Dictionary<string, object> { ["query"] = query }
);
if (!result.IsSuccess)
return (serverName, Enumerable.Empty<SearchResult>());
var items = JsonSerializer.Deserialize<List<SearchResult>>(result.Content)
?? Enumerable.Empty<SearchResult>();
return (serverName, items);
}
catch (Exception ex)
{
_logger.LogWarning(
ex,
"Failed to search server {ServerName}",
serverName
);
return (serverName, Enumerable.Empty<SearchResult>());
}
}
}
```
---
## Step 4: Connection Health Monitoring
**Generate health check service**:
```csharp
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Svrnty.MCP.Client.Core.Abstractions;
namespace MyApp.HealthChecks;
public class McpServerHealthCheck : IHealthCheck
{
private readonly IMcpClient _mcpClient;
private readonly ILogger<McpServerHealthCheck> _logger;
public McpServerHealthCheck(
IMcpClient mcpClient,
ILogger<McpServerHealthCheck> logger)
{
_mcpClient = mcpClient;
_logger = logger;
}
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
var servers = await _mcpClient.GetConnectedServersAsync();
var healthData = new Dictionary<string, object>();
var allHealthy = true;
foreach (var server in servers)
{
try
{
await _mcpClient.PingAsync(server.Name, cancellationToken);
healthData[server.Name] = "Healthy";
}
catch (Exception ex)
{
_logger.LogWarning(
ex,
"Health check failed for MCP server {ServerName}",
server.Name
);
healthData[server.Name] = $"Unhealthy: {ex.Message}";
allHealthy = false;
}
}
return allHealthy
? HealthCheckResult.Healthy("All MCP servers are responsive", healthData)
: HealthCheckResult.Degraded("Some MCP servers are not responsive", data: healthData);
}
}
```
**Register in Program.cs**:
```csharp
builder.Services.AddHealthChecks()
.AddCheck<McpServerHealthCheck>("mcp-servers");
app.MapHealthChecks("/health");
```
---
## Step 5: Validation & Testing
**Goal**: Verify configuration and provide feedback.
### 5.1 Configuration Validation
```bash
# Validate appsettings.json syntax
cat appsettings.json | jq .
# Test connection to configured server (if Stdio)
dotnet run --project /path/to/CodexMcpServer -- tools/list
```
### 5.2 Sample Test Execution
**Create integration test**:
```csharp
using Xunit;
using Microsoft.Extensions.DependencyInjection;
using Svrnty.MCP.Client.Core.Abstractions;
public class McpClientIntegrationTests
{
[Fact]
public async Task Client_CanConnectToServer()
{
// Arrange
var services = new ServiceCollection();
services.AddMcpClient(/* config */);
var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<IMcpClient>();
// Act
await client.ConnectToServerAsync("local-codex-server");
var tools = await client.ListToolsAsync("local-codex-server");
// Assert
Assert.NotEmpty(tools);
}
[Fact]
public async Task Client_CanCallTool()
{
// Arrange
var services = new ServiceCollection();
services.AddMcpClient(/* config */);
var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<IMcpClient>();
await client.ConnectToServerAsync("local-codex-server");
// Act
var result = await client.CallToolAsync(
"local-codex-server",
"search_codex",
new Dictionary<string, object>
{
["query"] = "test",
["maxResults"] = 5
}
);
// Assert
Assert.True(result.IsSuccess);
Assert.NotNull(result.Content);
}
}
```
### 5.3 Provide Feedback to User
**Generate summary report**:
```markdown
## Svrnty.MCP.Client Setup Complete
### Configuration Summary
- **Project Type**: ASP.NET Core Web API
- **Target Framework**: .NET 8.0
- **.NET SDK Version**: 8.0.100
### MCP Servers Configured
1. **local-codex-server** (Stdio)
- Command: `dotnet run --project /path/to/CodexMcpServer`
- Status: Enabled
- Timeout: 30 seconds
2. **remote-api-server** (HTTP)
- URL: https://api.example.com/mcp
- Status: Disabled (requires configuration)
- Timeout: 60 seconds
### Generated Files
- ✅ appsettings.json (MCP client configuration)
- ✅ Program.cs (client registration)
- ✅ Services/CodexSearchService.cs (search integration)
- ✅ Services/DocumentService.cs (document retrieval)
- ✅ HealthChecks/McpServerHealthCheck.cs (monitoring)
### Next Steps
1. Update server paths in appsettings.json
2. Configure authentication (if using HTTP transport)
3. Run tests: `dotnet test`
4. Start application: `dotnet run`
5. Check health: `curl http://localhost:5000/health`
### Testing the Integration
```bash
# List available tools
dotnet run -- mcp list-tools --server local-codex-server
# Call a tool
dotnet run -- mcp call-tool --server local-codex-server --tool search_codex --args '{"query":"test"}'
```
### Troubleshooting
- Ensure MCP servers are running before connecting
- Check logs in `logs/mcp-client.log` for connection errors
- Verify timeout values are appropriate for your servers
- Test network connectivity for HTTP-based servers
```
---
## Step 6: AI Agent Workflow
**Recommended automation sequence**:
1. **Analyze** → Run Step 1 (system detection)
2. **Confirm** → Show detected features, ask user approval
3. **Generate** → Create files from Step 2-4
4. **Validate** → Run Step 5 tests
5. **Report** → Provide Step 5.3 summary
6. **Handoff** → "Configuration complete. Ready to start using MCP client?"
**Example AI interaction**:
```
AI: I've analyzed your project. Here's what I found:
- .NET 8.0 Web API
- Existing HTTP client setup
- Logging configured
I can set up Svrnty.MCP.Client to connect to:
1. Local CODEX server (Stdio)
2. Remote API server (HTTP)
Should I proceed with configuration? (yes/no)
User: yes
AI: Generating configuration files...
✅ appsettings.json created
✅ Program.cs updated
✅ Sample services created
✅ Health checks configured
Running validation tests...
✅ All tests passed
Setup complete! Your app can now connect to MCP servers.
Next step: Run `dotnet run` to start.
```
---
## Appendix: Common Scenarios
### A. Console Application Setup
- Use `IHostBuilder` pattern
- Register MCP client in service collection
- Call tools from `Main` method
### B. Background Worker Setup
- Implement `IHostedService`
- Connect to servers on startup
- Poll tools periodically
### C. Web API Integration
- Register client in DI container
- Inject into controllers/services
- Call tools in request handlers
### D. Multiple Server Scenarios
- Configure multiple servers in array
- Use `GetConnectedServersAsync()` to discover
- Aggregate results from multiple sources
---
**Document Version**: 1.0.0
**Last Updated**: 2025-10-19
**Target**: Svrnty.MCP.Client