Back to blog

AI patterns

C# / .NET MCP Server Demo

Anthropic's Model Context Protocol (MCP) is becoming a standard way to integrate AI models with external tools and data sources. Most of the available demo implementations are in Python or TypeScript β€” this C#/.NET project shows how MCP fits cleanly into the Microsoft ecosystem.

SvK by Sven von KΓ€nel 14 min read
  • KI
  • MCP
  • .NET
  • C#

MCP support for multiple hosts and AI providers

Anthropic's Model Context Protocol (MCP) is becoming a standard way to integrate AI models with external tools and data sources. Most of the available demo implementations are in Python or TypeScript β€” this C#/.NET project shows how MCP fits cleanly into the Microsoft ecosystem.

This demo project includes a full MCP host implementation in .NET 9.0 that integrates commercial AI providers such as OpenAI, Azure OpenAI and Ionos as well as local LLM solutions via Ollama and LMStudio. The architecture lets developers build both STDIO- and SSE-based MCP servers, wire them into different MCP hosts, and use different AI providers along the way.

What the C#/.NET implementation brings:

  • Microsoft.Extensions.AI for a unified AI provider abstraction

  • Dependency Injection following established .NET patterns

  • Entity Framework Core for database-backed tools

  • OpenTelemetry for comprehensive observability

  • Docker Compose for containerised deployment

Project overview

What does the project contain?

The evanto MCP Host System is a demo .NET application that implements the Model Context Protocol (MCP) to provide a unified interface between AI chat providers and specialised business tools. It allows seamless integration between several AI providers (OpenAI, Azure, Ollama, etc.) and custom business tools β€” here illustrated with a support-ticket-management example and support-document processing.

Core capabilities

  • Multi-provider AI integration: support for OpenAI, Azure OpenAI, Ollama, LMStudio and Ionos

  • Support-ticket management: full CRUD operations for support requests with SQLite storage

  • Document vectorisation: PDF processing and semantic search with the Qdrant vector database for support documentation PDFs

  • Interactive chat client: command-line interface for AI conversations with tool integration

  • Containerised deployment: Docker Compose setup for simple deployment and scaling

  • Comprehensive tests: built-in MCP server test framework with automated parameters

Target audience

This system is designed for C# developers who want to:

  • integrate AI capabilities into their applications via Microsoft.Extensions.AI

  • build MCP-compliant tools and servers

  • implement semantic search and document processing

  • create multi-provider AI chat systems

  • deploy containerised AI infrastructure

AI developer support

Certain parts of the project, such as vectorisation and PDF document analysis, were initially generated with Claude Code and then edited by hand. The instructions for the coding AI are kept in CLAUDE.md and CodingRules.md (see the GitHub repository).

System architecture

High-level architecture

The overview below groups the various projects in the demo system:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Evanto MCP Host System                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                     Applications Layer                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ cmd-mcp-host    β”‚  β”‚ cmd-vectorize   β”‚  β”‚ MCP Servers     β”‚  β”‚
β”‚  β”‚ (Interactive    β”‚  β”‚ (PDF            β”‚  β”‚ (SSE/STDIO)     β”‚  β”‚
β”‚  β”‚  Client)        β”‚  β”‚  Processing)    β”‚  β”‚                 β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                    Core Libraries Layer                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Evanto.Mcp.Host β”‚  β”‚ Evanto.Mcp.Apps β”‚  β”‚ Evanto.Mcp.     β”‚  β”‚
β”‚  β”‚ (Factories &    β”‚  β”‚ (App Helpers)   β”‚  β”‚ Common          β”‚  β”‚
β”‚  β”‚  Testing)       β”‚  β”‚                 β”‚  β”‚ (Settings)      β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                External Integration Layer                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Evanto.Mcp.     β”‚  β”‚ Evanto.Mcp.     β”‚  β”‚ Evanto.Mcp.     β”‚  β”‚
β”‚  β”‚ Embeddings      β”‚  β”‚ Pdfs            β”‚  β”‚ QdrantDB        β”‚  β”‚
β”‚  β”‚ (Multi-Provider)β”‚  β”‚ (iText7 Wrapper)β”‚  β”‚ (Vector DB)     β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                     MCP Tools Layer                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”‚
β”‚  β”‚ SupportWizard   β”‚  β”‚ SupportDocs     β”‚                       β”‚
β”‚  β”‚ (Ticket System) β”‚  β”‚ (Doc Search)    β”‚                       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Core components

  1. Applications: standalone executable programs

  2. Core libraries: business logic and infrastructure

  3. External integration: wrappers for external dependencies

  4. MCP tools: domain-specific tool implementations

Technology stack

  • .NET 9.0: current C# features

  • Microsoft.Extensions.AI: unified AI provider abstractions

  • Model Context Protocol: official MCP client/server implementation

  • Entity Framework Core: database abstraction with SQLite

  • OpenTelemetry: observability and telemetry

  • Docker & Docker Compose: containerisation and orchestration

  • iText7: PDF processing and text extraction

  • Qdrant: vector database for semantic search

Design patterns

  • Factory pattern: AI client and MCP client creation

  • Repository pattern: data access abstraction

  • Dependency Injection: Microsoft.Extensions.DependencyInjection

  • Clean Architecture: separation of concerns across layers

  • Configuration-driven: heavy use of appsettings.json

Project structure

Overview of the GitHub project:

public-ai/
β”œβ”€β”€ app/                                     # Standalone applications
β”‚   β”œβ”€β”€ cmd-mcp-host/                        # Interactive MCP client
β”‚   β”‚   β”œβ”€β”€ Program.cs                       # Main entry point
β”‚   β”‚   β”œβ”€β”€ appsettings.json                 # Client configuration
β”‚   β”‚   └── system-prompt.txt                # AI system prompt
β”‚   └── cmd-vectorize/                       # PDF vectorisation utility
β”‚       β”œβ”€β”€ Program.cs                       # Main entry point
β”‚       β”œβ”€β”€ appsettings.json                 # Vectorisation settings
β”‚       └── Services/                        # Processing services
β”œβ”€β”€ lib/                                     # Core libraries
β”‚   β”œβ”€β”€ Evanto.Mcp.Common/                   # Shared utilities
β”‚   β”‚   β”œβ”€β”€ Settings/                        # Configuration models
β”‚   β”‚   β”œβ”€β”€ Mcp/                             # MCP base classes
β”‚   β”‚   └── Extensions/                      # Extension methods
β”‚   β”œβ”€β”€ Evanto.Mcp.Host/                     # MCP hosting infrastructure
β”‚   β”‚   β”œβ”€β”€ Factories/                       # Factory implementations
β”‚   β”‚   β”œβ”€β”€ Tests/                           # Test framework
β”‚   β”‚   └── Models/                          # Core models
β”‚   β”œβ”€β”€ Evanto.Mcp.Apps/                     # Application helpers
β”‚   β”‚   β”œβ”€β”€ EvBaseAppHelper.cs               # Base application logic
β”‚   β”‚   β”œβ”€β”€ EvCmdAppHelper.cs                # Command-line app helper
β”‚   β”‚   └── EvSrvAppHelper.cs                # Server app helper
β”‚   β”œβ”€β”€ Evanto.Mcp.Embeddings/               # Text embedding services
β”‚   β”‚   β”œβ”€β”€ Factories/                       # Multi-provider factory
β”‚   β”‚   β”œβ”€β”€ Services/                        # Embedding implementations
β”‚   β”‚   └── Extensions/                      # DI extensions
β”‚   β”œβ”€β”€ Evanto.Mcp.Pdfs/                     # PDF processing
β”‚   β”‚   β”œβ”€β”€ Services/                        # iText7 wrapper
β”‚   β”‚   └── Extensions/                      # DI extensions
β”‚   β”œβ”€β”€ Evanto.Mcp.QdrantDB/                 # Vector database
β”‚   β”‚   β”œβ”€β”€ Repository/                      # Qdrant repository
β”‚   β”‚   β”œβ”€β”€ Models/                          # Document models
β”‚   β”‚   └── Extensions/                      # DI extensions
β”‚   β”œβ”€β”€ Evanto.Mcp.Tools.SupportWizard/      # Support ticket system
β”‚   β”‚   β”œβ”€β”€ Tools/                           # MCP tool implementations
β”‚   β”‚   β”œβ”€β”€ Repository/                      # Database repository
β”‚   β”‚   β”œβ”€β”€ Models/                          # Entity models
β”‚   β”‚   └── Context/                         # EF Core context
β”‚   └── Evanto.Mcp.Tools.SupportDocs/        # Document search tools
β”‚       β”œβ”€β”€ Tools/                           # MCP tool implementations
β”‚       └── Extensions/                      # DI extensions
β”œβ”€β”€ srv/                                     # MCP servers
β”‚   β”œβ”€β”€ sse-mcp-server/                      # SSE-based MCP server
β”‚   β”‚   β”œβ”€β”€ Program.cs                       # ASP.NET Core app
β”‚   β”‚   β”œβ”€β”€ appsettings.json                 # Server configuration
β”‚   β”‚   └── Dockerfile                       # Container definition
β”‚   └── stdio-mcp-server/                    # STDIO-based MCP server
β”‚       β”œβ”€β”€ Program.cs                       # Console host app
β”‚       β”œβ”€β”€ appsettings.json                 # Server configuration
β”‚       └── Dockerfile                       # Container definition
β”œβ”€β”€ db/                                      # Database files
β”‚   └── ev-supportwizard.db                  # SQLite database (auto-created)
β”œβ”€β”€ pdfs/                                    # PDF documents
β”‚   └── processed_files.json                 # File tracking
β”œβ”€β”€ run/                                     # Runtime configurations
β”‚   β”œβ”€β”€ sse/appsettings.json                 # SSE server config
β”‚   └── stdio/appsettings.json               # STDIO server config
β”œβ”€β”€ Directory.Packages.props                 # Central package management
β”œβ”€β”€ docker-compose.yaml                      # Container orchestration
β”œβ”€β”€ .env.example                             # Environment variables template
└── CLAUDE.md                                # AI assistant instructions

Core libraries

Evanto.Mcp.Host

Purpose: core MCP hosting infrastructure with factories and test framework

Key components:

  • EvMcpClientFactory: creates MCP clients for different transport types (STDIO, SSE, HTTP)

  • EvChatClientFactory: creates AI chat clients for multiple providers

  • EvMcpServerTester: comprehensive test framework for MCP servers and tools

Usage example:

// Create MCP client
var mcpClient = await mcpClientFactory.CreateAsync(serverSettings);

// Create chat client
var chatClient = chatClientFactory.Create("OpenAI");

// Test MCP server
var testResult = await mcpTester.TestServerAsync(serverSettings);

Evanto.Mcp.Common

Purpose: shared configuration models, settings and utilities

Key components:

  • EvHostAppSettings: main application configuration

  • EvChatClientSettings: AI provider configurations

  • EvMcpServerSettings: MCP server configurations

  • EvMcpToolBase: base class for MCP tool implementations

Evanto.Mcp.Apps

Purpose: application helper services and shared functionality

Key components:

  • EvBaseAppHelper: common application initialisation

  • EvCmdAppHelper: command-line application helper

  • EvSrvAppHelper: server application helper

Evanto.Mcp.Embeddings

Purpose: multi-provider text embedding services with Microsoft.Extensions.AI

Key features:

  • Multi-provider support: OpenAI, Azure, Ollama, LMStudio, Ionos

  • Unified interface: single API regardless of the provider

  • Performance optimisation: built-in caching and rate limiting

  • Configuration-driven: provider selection via settings

Usage example:

// Register embedding service
services.AddEmbeddingService(settings);

// Use embedding service
var embeddings = await embeddingService.GenerateEmbeddingsAsync(texts);

Evanto.Mcp.Pdfs

Purpose: PDF text extraction services with iText7

Key features:

  • Enterprise PDF processing: handles complex PDF structures

  • Service abstraction: clean interface that hides iText7 complexity

  • Error handling: robust handling of corrupt PDFs

  • Performance-optimised: efficient text extraction

Usage example:

// Register PDF service
services.AddPdfTextExtractor();

// Extract text from a PDF
var text = await pdfExtractor.ExtractTextAsync(pdfPath);

Evanto.Mcp.QdrantDB

Purpose: unified repository for Qdrant vector database operations

Key features:

  • Unified document model: a single EvDocument for all operations

  • Advanced search: vector, text and combined search queries

  • Metadata support: rich document metadata and filtering

  • Repository pattern: clean data-access abstraction

Usage example:

// Register Qdrant repository services.AddQdrantDocumentRepository(settings);

// Store a document await repository.StoreDocumentAsync(document);

// Search documents var results = await repository.SearchDocumentsAsync(query);

Evanto.Mcp.Tools.SupportWizard

Purpose: support-ticket-management system with SQLite database

Key features:

  • Full CRUD operations: create, read, update, delete support requests

  • User management: support staff with topic assignments

  • Status tracking: ticket lifecycle management

  • Entity Framework Core: code-first database approach

Demo database schema (adaptable):

-- Support requests
CREATE TABLE SupportRequests (
    Id UNIQUEIDENTIFIER PRIMARY KEY,
    CustomerEmail TEXT NOT NULL,
    CustomerName TEXT NOT NULL,
    Subject TEXT NOT NULL,
    Description TEXT NOT NULL,
    Status INTEGER NOT NULL,
    Priority INTEGER NOT NULL,
    CreatedAt DATETIME NOT NULL,
    UpdatedAt DATETIME NOT NULL
);

-- Users
CREATE TABLE Users (
    Id UNIQUEIDENTIFIER PRIMARY KEY,
    Name TEXT NOT NULL,
    Email TEXT NOT NULL,
    Topic TEXT NOT NULL,
    IsActive BOOLEAN NOT NULL
);

Evanto.Mcp.Tools.SupportDocs

Purpose: document search and management with semantic similarity

Key features:

  • Semantic search: find documents by meaning, not just by keywords

  • Document management: store and organise documentation

  • Vector integration: uses Qdrant for high-performance search

  • Multi-provider embeddings: flexible embedding provider support

Applications

cmd-mcp-host

Purpose: interactive MCP client with AI chat integration

Key features:

  • Multi-provider chat: switch between OpenAI, Azure, Ollama, etc.

  • MCP tool integration: access to SupportWizard and SupportDocs tools

  • Interactive interface: a rich console experience with Spectre.Console

  • Configuration management: supports several AI providers in parallel

Usage:

# Run the interactive client
dotnet run --project app/cmd-mcp-host

# Show help
dotnet run --project app/cmd-mcp-host -- --help

# List available providers
dotnet run --project app/cmd-mcp-host -- --list

# Run server tests
dotnet run --project app/cmd-mcp-host -- --test

cmd-vectorize

Purpose: PDF processing and vectorisation utility

Key features:

  • Batch PDF processing: process multiple PDFs in one run

  • Text chunking: configurable chunk sizes and overlap

  • Vector storage: store embeddings in the Qdrant database

  • File tracking: avoid reprocessing with a JSON tracking file

Usage:

# Process PDFs in the configured directory
dotnet run --project app/cmd-vectorize

# Configuration in appsettings.json:
{
    "PdfDirectory": "../../../../../pdfs",
    "TrackingFilePath": "../../../../../pdfs/processed_files.json"
}

MCP servers

sse-mcp-server

Purpose: HTTP/SSE-based MCP server for web integration

Key features:

  • ASP.NET Core: modern web server infrastructure

  • Server-Sent Events: real-time communication

  • Health checks: built-in endpoint monitoring

  • Docker support: containerised deployment

stdio-mcp-server

Purpose: STDIO-based MCP server for command-line integration

Key features:

  • Standard I/O: works with any MCP client

  • Console host: lightweight deployment

  • Docker support: container-based execution

  • Interactive mode: TTY support for debugging

Prerequisites and setup

System requirements

  • .NET 9.0 SDK or later

  • Docker 20.10 or later

  • Docker Compose 2.0 or later

  • Git for version control

Required external services

  1. Qdrant vector database: provided via Docker Compose

  2. AI provider API keys: at least one of:

    • OpenAI API key

    • Azure OpenAI credentials

    • Ollama (local installation)

    • LMStudio (local installation)

    • Ionos AI API key

Development tools (recommended)

  • Visual Studio 2022 (updated) or VS Code with C# extension

  • Docker Desktop for container management

  • Postman or similar for API testing

  • DB Browser for SQLite for database inspection

Installation steps

  1. Clone the repository:
git clone https://github.com/svkaenel/public-ai
cd public-ai
  1. Verify the .NET installation:
dotnet --version
# Should show 9.0.x or later
  1. Restore packages:
dotnet restore
  1. Build the solution:
dotnet build
  1. Set up environment variables (see configuration guide)

Configuration guide

Environment variable setup

The system uses environment variables for API keys and sensitive configuration. This approach keeps secrets out of source code and supports different environments.

Step 1: Create a .env file

# Copy the example file
cp .env.example .env

# Edit with your values
nano .env  # or your preferred editor

Step 2: Configure API keys

Edit the .env file with your API keys:

# OpenAI API key
OPENAI_API_KEY=your-openai-api-key-here

# Ionos AI API key (JWT token)
IONOS_API_KEY=your-ionos-jwt-token-here

# Azure AI API key
AZURE_API_KEY=your-azure-ai-api-key-here

# Azure OpenAI API key
AZUREOAI_API_KEY=your-azure-openai-api-key-here

# LMStudio API key (usually empty for local)
LMSTUDIO_API_KEY=

# Ollama API key (usually empty for local)
OLLAMA_API_KEY=

# Docker Compose configuration
SSE_PORT=5561
SSE_CONFIG_PATH=./run/sse/appsettings.json
STDIO_CONFIG_PATH=./run/stdio/appsettings.json

Step 3: Verify the configuration

The system loads environment variables automatically in this priority order:

  1. Command-line environment variables (highest priority)

  2. System environment variables

  3. .env file variables

  4. appsettings.json values (lowest priority)

AI provider configuration

The ChatClients section in appsettings.json configures AI providers:

{
  "DefaultChatClient": "OpenAI",
  "ChatClients": [
    {
      "ProviderName": "OpenAI",
      "Endpoint": "https://api.openai.com/v1",
      "DefaultModel": "o4-mini",
      "AvailableModels": [
        "o4-mini",
        "gpt-4.1-mini",
        "gpt-4.1",
        "o1"
      ]
    },
    {
      "ProviderName": "Azure",
      "Endpoint": "https://your-resource.services.ai.azure.com/models",
      "DefaultModel": "DeepSeek-R1",
      "AvailableModels": [
        "DeepSeek-R1"
      ]
    },
    {
      "ProviderName": "Ollama",
      "Endpoint": "http://localhost:11434",
      "DefaultModel": "qwen3:14b",
      "AvailableModels": [
        "qwen3:4b",
        "qwen3:14b",
        "gemma3:12b"
      ]
    }
  ]
}

Database configuration

SQLite databases are configured via connection strings:

{
  "ConnectionStrings": {
    "SupportWizardDB": "Filename=db/ev-supportwizard.db"
  }
}

OpenTelemetry configuration

Configure observability and telemetry:

{
  "Telemetry": {
    "Enabled": true,
    "ServiceName": "cmd-mcp-host",
    "OtlpEndpoint": "http://localhost:4317",
    "EnableConsoleExporter": false,
    "EnableOtlpExporter": true,
    "LogSensitiveData": false,
    "ActivitySources": [
      "Microsoft.Extensions.AI"
    ]
  }
}

Docker setup and deployment

Docker Compose overview

The system uses Docker Compose to orchestrate several services:

  • qdrantdb: vector database for document embeddings

  • aspire-dashboard: .NET Aspire dashboard for telemetry

  • sse-mcp-server: HTTP/SSE MCP server

  • stdio-mcp-server: STDIO MCP server

Environment preparation

  1. Set up environment variables:
cp .env.example .env
# Edit .env with your API keys
  1. Create runtime configuration directories:
mkdir -p run/sse run/stdio
  1. Copy (and adjust!) configuration files:
# Copy example configurations
cp app/cmd-mcp-host/appsettings.json run/sse/
cp app/cmd-mcp-host/appsettings.json run/stdio/

Step-by-step deployment

Step 1: Build and start services

# Build and start all services
docker-compose up -d

# View logs
docker-compose logs -f

# Check service status
docker-compose ps

Build the MCP servers separately (from the public-ai directory):

# SSE MCP server
docker build -f srv/sse-mcp-server/Dockerfile -t sse-mcp-server .

# STDIO MCP server  
docker build -f srv/stdio-mcp-server/Dockerfile -t stdio-mcp-server .

Step 2: Verify services

# Check that Qdrant is running
curl http://localhost:6335/

# Check the SSE MCP server
curl http://localhost:5561/

# Check the Aspire dashboard
# Open in browser: http://localhost:4316

Step 3: Initialise the database

The MCP servers create and migrate the SQLite database automatically on first start.

First-steps workflow

Follow these steps to get the system running from scratch.

Step 1: Clone and build

# Clone the repository
git clone https://github.com/svkaenel/public-ai
cd public-ai

# Build the solution
dotnet build

Step 2: Configure the environment

# Create the environment file
cp .env.example .env

# Edit with your API keys
nano .env

# Set at least one AI provider:
# OPENAI_API_KEY=your-api-key
# or
# OLLAMA_API_KEY=  # for local Ollama

Step 3: Start the Docker services

# Start infrastructure services
docker-compose up -d

# Verify that services are running
docker-compose ps

# Check logs if needed
docker-compose logs -f

Step 4: Populate the vector database

# Place PDF files in the pdfs/ directory
cp /path/to/your/docs/*.pdf pdfs/

# Run vectorisation
dotnet run --project app/cmd-vectorize

# Check processing results
cat pdfs/processed_files.json

Step 5: Test the MCP servers

# Test all MCP servers and tools
dotnet run --project app/cmd-mcp-host -- --test

# List available providers
dotnet run --project app/cmd-mcp-host -- --list

Step 6: Run the interactive client

# Start the interactive chat client
dotnet run --project app/cmd-mcp-host

# Example conversation:
# > Hi, can you help me find documents about embeddings?
# > Create a new support ticket for customer john@example.com
# > Show me all support tickets with high priority

PDF vectorisation guide

Purpose

The cmd-vectorize utility processes PDF documents and converts them into vector embeddings for semantic search. This lets the SupportDocs tool find relevant documents based on meaning rather than keywords alone.

Configuration

Edit app/cmd-vectorize/appsettings.json:

{
  "PdfDirectory": "../../../../../pdfs",
  "TrackingFilePath": "../../../../../pdfs/processed_files.json",
  "DefaultEmbeddingProvider": "OpenAI",
  "DefaultEmbeddingProviderAlt": "OllamaSharp",
  "EmbeddingProviders": [
    {
      "ProviderName": "OpenAI",
      "Endpoint": "https://api.openai.com/v1",
      "DefaultModel": "text-embedding-3-small",
      "EmbeddingDimensions": 1536
    },
    {
      "ProviderName": "Ollama",
      "Endpoint": "http://localhost:11434",
      "DefaultModel": "nomic-embed-text",
      "EmbeddingDimensions": 768
    }
  ],
  "Qdrant": {
    "QdrantEndpoint": "localhost",
    "CollectionName": "ev_support_documents",
    "QdrantPort": 6336,
    "VectorDimension": 1536
  }
}

Usage workflow

Step 1: Prepare PDF files

# Create the pdfs directory (if not present)
mkdir -p pdfs

# Copy your PDF documents
cp /path/to/your/docs/*.pdf pdfs/

# Example structure:
# pdfs/
# β”œβ”€β”€ user-manual.pdf
# β”œβ”€β”€ api-documentation.pdf
# β”œβ”€β”€ troubleshooting.pdf
# └── processed_files.json (generated automatically)

Step 2: Run vectorisation

# Process all PDFs in the directory
dotnet run --project app/cmd-vectorize

# Expected output:
# [2024-01-01 10:00:00] Starting PDF vectorisation...
# [2024-01-01 10:00:01] Processing: user-manual.pdf
# [2024-01-01 10:00:05] 1,234 words extracted, 5 chunks created
# [2024-01-01 10:00:10] Embeddings generated and stored in Qdrant
# [2024-01-01 10:00:10] Processing: api-documentation.pdf
# [2024-01-01 10:00:15] Skipping (already processed): troubleshooting.pdf
# [2024-01-01 10:00:15] Vectorisation complete!

Step 3: Check the results

# Check the tracking file
cat pdfs/processed_files.json

# Example content:
{
  "processed_files": [
    {
      "filename": "user-manual.pdf",
      "processed_at": "2024-01-01T10:00:10Z",
      "chunk_count": 5,
      "word_count": 1234
    }
  ]
}

Using the MCP host client

Interactive chat interface

The cmd-mcp-host application provides a rich interactive experience for AI conversations with integrated MCP tools.

Start the client

# Run with default configuration
dotnet run --project app/cmd-mcp-host

# Show available options
dotnet run --project app/cmd-mcp-host -- --help

# List configured providers
dotnet run --project app/cmd-mcp-host -- --list

# Test MCP servers before starting
dotnet run --project app/cmd-mcp-host -- --test

Basic usage

Welcome to the Evanto MCP Host Client
Current provider: OpenAI (o4-mini)
Available tools: SupportWizard, SupportDocs
Type 'help' for commands, 'exit' to quit

> Hi, can you help me with support tickets?

Tool integration commands

The client integrates automatically with MCP tools. You can use natural language to interact with them:

# SupportWizard tool examples
> "Create a new support ticket for customer john@example.com with subject 'login issues'"
> "Show me all support tickets with high priority"
> "List all users who can handle technical issues"
> "Update support ticket ID 123 to resolved"

# SupportDocs tool examples
> "Search for documentation about embeddings"
> "Find information about API authentication"
> "Look up troubleshooting guides for database connections"

For further technical detail, full configuration options and advanced usage scenarios, see the full README.md in our GitHub repository.

The project is available under the MIT licence on GitHub: https://github.com/svkaenel/public-ai

This C#/.NET MCP demo project shows how to implement an MCP integration in the Microsoft ecosystem and provides a foundation for your own MCP-based applications.

NEWSLETTER

Four to six times a year, no marketing noise.

One pattern, one case, one recommendation. Signup with double opt-in, unsubscribe at any time.