Building a Remote MCP Server with .NET 10 and Prompts

Building a Remote MCP Server with .NET 10 and Prompts

Model Context Protocol (MCP) gives AI clients a standard way to discover and call server capabilities. Most examples focus on tools first, but this project demonstrates an equally important direction: prompt-first server design.

In this post, we walk through a practical ASP.NET Core implementation that exposes:

  • tool operations (for executable tasks)
  • reusable prompts (for instruction templates and interaction quality)
  • HTTP MCP transport for remote clients
  • lightweight operational endpoints for health and diagnostics

By the end, you will understand how this server is structured, how prompts are registered and invoked, and how to expand the design for production scenarios.

Why Prompt-First MCP Servers Matter

Tools answer what the server can do. Prompts shape how those capabilities are used.

In assistant systems, quality often depends on strong instruction templates:

  • consistent phrasing
  • clear required input
  • predictable output structure
  • fallback behavior when user requests are ambiguous

When prompts are exposed through MCP, clients can discover them dynamically and compose better experiences without hardcoding all instructions in the client itself.

This creates a cleaner split of responsibilities:

  • server owns domain guidance and prompt governance
  • client owns orchestration and user interface

Get Started

The complete, production-ready source code is available on GitHub:

remote-MCP-servers-using-dotnet-sdk-prompts

Clone it locally:

git clone https://github.com/azurecorner/remote-MCP-servers-using-dotnet-sdk-prompts.git

In the repository you’ll find:

  • Full source code for tool, prompt, and resource implementations
  • PowerShell scripts for testing MCP capabilities
  • Configuration files for local VS Code MCP client integration
  • Examples ready to extend and adapt for your own domain

Project Overview

This repository contains a remote MCP server built on:

  • ASP.NET Core
  • ModelContextProtocol.AspNetCore
  • .NET 10

Main capabilities:

  • MCP endpoint at /mcp
  • health check endpoint at /api/healthz
  • weather tool integration through Open-Meteo
  • prompt discovery and retrieval through prompts/list and prompts/get

Core source layout:

  • src/McpServer/McpServer: MCP host app, tools, prompts, resources, startup
  • src/McpServer/WeatherService: external weather API integration
  • scripts: PowerShell automation for MCP protocol calls

Runtime and Transport Design

The server is configured with both HTTP transport and stdio transport. HTTP is the primary remote integration path, while stdio can be useful for local tool-chain and debugging workflows.

At startup, the app:

  1. reads FUNCTIONS_CUSTOMHANDLER_PORT (default 8081)
  2. binds Kestrel to 0.0.0.0:port
  3. registers tools, resources, and prompts in the MCP pipeline
  4. maps /mcp for protocol traffic
  5. maps /api/healthz for liveness checks

This split is operationally useful:

  • /mcp handles AI protocol traffic
  • /api/healthz supports probes and platform health monitors

Prompt Architecture Deep Dive

Prompt definitions live in a dedicated prompt container class decorated with an MCP prompt type attribute.

Prompt Container Responsibilities

The prompt class has three key responsibilities:

  • define discoverable prompt methods
  • document methods and arguments with metadata
  • optionally log invocation context for observability

The implementation includes dependency-injected logging, making it straightforward to trace usage patterns in real deployments.

Prompt 1: default_prompt

Purpose:

  • provide a baseline system instruction for concise, reliable responses

Behavior:

  • returns a compact instruction block emphasizing brevity and non-hallucination
  • includes explicit uncertainty handling by asking for clarification when needed

Why this is valuable:

  • offers a reusable default instruction that clients can apply consistently
  • avoids duplicating base instruction text across multiple clients

Prompt 2: weather_query_guide

Purpose:

  • teach users or orchestrators how to ask weather questions effectively

Behavior:

  • returns structured guidance with examples and best practices
  • accepts optional userContext and appends it when provided

Why argumentized prompts are powerful:

  • one prompt template can adapt to locale, user preferences, or interaction history
  • client-side logic remains simple while server controls guidance quality

Prompt 3: weather_data_interpretation

Purpose:

  • standardize how weather tool output should be interpreted and presented

Behavior:

  • explains expected weather fields and presentation strategy
  • includes recommendation patterns, for example suggesting umbrella advice when appropriate

Why this helps:

  • decouples raw tool output from user-facing narrative quality
  • improves consistency across multi-client environments

Prompt Discovery and Invocation Flow

MCP interaction is straightforward:

  1. Client calls prompts/list
  2. Server returns prompt metadata including names, descriptions, and argument schema
  3. Client calls prompts/get with a selected prompt name (and optional arguments)
  4. Server returns prompt content ready for orchestration

This means client authors can build dynamic UIs or agent planners that adapt to server capabilities at runtime, without shipping fixed prompt catalogs.

Local Run and Verification

From repo root:

dotnet restore .\src\McpServer\McpServer.slnx
dotnet run --project .\src\McpServer\McpServer\McpServer.csproj

Default MCP endpoint:

  • http://0.0.0.0:8081/mcp

Health check:

  • http://localhost:8081/api/healthz

Then, in a second terminal, verify protocol behavior with scripts.

Script-Driven MCP Validation

The scripts folder includes ready-to-use protocol calls for tools, prompts, and optional resources.

List prompts:

.\scripts\list-mcp-server-prompts.ps1

Get a specific prompt:

.\scripts\call-mcp-prompt.ps1 -PromptName "default_prompt"

Get weather query guide:

.\scripts\call-mcp-prompt.ps1 -PromptName "weather_query_guide"

Get weather interpretation guide:

.\scripts\call-mcp-prompt.ps1 -PromptName "weather_data_interpretation"

Validate tools too:

.\scripts\list-mcp-server-tools.ps1
.\scripts\call-mcp-tool.ps1 -toolName "ping" -toolParams @{ message = "hello from test" }
.\scripts\call-mcp-tool.ps1 -toolName "get_weather" -toolParams @{ city = "Paris" }

How to Add a New Prompt Correctly

Recommended process:

  1. Add a method to the prompt container class.
  2. Decorate it as an MCP prompt.
  3. Add clear descriptions on method and arguments.
  4. Return string or Task<string>.
  5. Keep output deterministic and easy for clients to reuse.
  6. Verify with prompts/list and prompts/get.
[McpServerPrompt]
[Description("Provides concise guidance for travel weather planning")]
public Task<string> TravelWeatherGuide(
    [Description("Destination city")] string city,
    [Description("Optional number of travel days")] int? days = null)
{
    var output = $"""
    # Travel Weather Guide

    Destination: {city}
    Duration: {(days.HasValue ? $"{days} day(s)" : "unspecified")}

    Ask for:
    - daily highs/lows
    - rain probability
    - wind conditions
    """;

    return Task.FromResult(output);
}

Defined in src/McpServer/McpServer/McpServerPrompts.cs and registered in src/McpServer/McpServer/Program.cs with:

.WithPrompts<McpServerPrompts>();

Gora LEYE

I'm a microsoft most valuable professional (MVP) .NET Architect and Technical Expert skills located in Paris (FRANCE). The purpose of this blog is mainly to post general .NET tips and tricks, www.masterconduite.com Gora LEYE

Support us

BMC logoBuy me a coffee