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
Applications: standalone executable programs
Core libraries: business logic and infrastructure
External integration: wrappers for external dependencies
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 providersEvMcpServerTester: 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 configurationEvChatClientSettings: AI provider configurationsEvMcpServerSettings: MCP server configurationsEvMcpToolBase: base class for MCP tool implementations
Evanto.Mcp.Apps
Purpose: application helper services and shared functionality
Key components:
EvBaseAppHelper: common application initialisationEvCmdAppHelper: command-line application helperEvSrvAppHelper: 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
EvDocumentfor all operationsAdvanced 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
Qdrant vector database: provided via Docker Compose
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
- Clone the repository:
git clone https://github.com/svkaenel/public-ai
cd public-ai
- Verify the .NET installation:
dotnet --version
# Should show 9.0.x or later
- Restore packages:
dotnet restore
- Build the solution:
dotnet build
- 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:
Command-line environment variables (highest priority)
System environment variables
.env file variables
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
- Set up environment variables:
cp .env.example .env
# Edit .env with your API keys
- Create runtime configuration directories:
mkdir -p run/sse run/stdio
- 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.