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

19 KiB

AGENT-PRIMER: OpenHarbor.MCP.Client Automated Setup

Purpose: This document guides AI agents to automatically analyze a target system and configure OpenHarbor.MCP.Client integration with minimal human intervention.

Target Audience: AI assistants (Claude, ChatGPT, etc.) helping developers integrate MCP client capabilities into .NET applications.


Overview

OpenHarbor.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

# 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

# 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

# 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

{
  "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:

<ItemGroup>
  <!-- Core MCP Client library -->
  <ProjectReference Include="/path/to/OpenHarbor.MCP.Client.Core/OpenHarbor.MCP.Client.Core.csproj" />

  <!-- Infrastructure (transports, connection management) -->
  <ProjectReference Include="/path/to/OpenHarbor.MCP.Client.Infrastructure/OpenHarbor.MCP.Client.Infrastructure.csproj" />

  <!-- ASP.NET Core integration (if applicable) -->
  <ProjectReference Include="/path/to/OpenHarbor.MCP.Client.AspNetCore/OpenHarbor.MCP.Client.AspNetCore.csproj" />
</ItemGroup>

Note: When OpenHarbor.MCP.Client is published to NuGet, replace with:

<ItemGroup>
  <PackageReference Include="OpenHarbor.MCP.Client.AspNetCore" Version="1.0.0" />
</ItemGroup>

2.2 appsettings.json Configuration

Generate based on detected project:

{
  "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:

using OpenHarbor.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:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using OpenHarbor.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:

using OpenHarbor.MCP.Client.Core.Abstractions;
using OpenHarbor.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:

using OpenHarbor.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):

using OpenHarbor.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:

using Microsoft.Extensions.Diagnostics.HealthChecks;
using OpenHarbor.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:

builder.Services.AddHealthChecks()
    .AddCheck<McpServerHealthCheck>("mcp-servers");

app.MapHealthChecks("/health");

Step 5: Validation & Testing

Goal: Verify configuration and provide feedback.

5.1 Configuration Validation

# 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:

using Xunit;
using Microsoft.Extensions.DependencyInjection;
using OpenHarbor.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:

## OpenHarbor.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 OpenHarbor.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**: OpenHarbor.MCP.Client