A feature-complete Model Context Protocol (MCP) server template in C# using the official csharp-sdk. This starter demonstrates all major MCP features with clean, idiomatic C# code leveraging .NET 8 and dependency injection.
| Category | Feature | Description |
|---|---|---|
| Tools | hello |
Basic tool with annotations |
get_weather |
Tool returning structured JSON | |
ask_llm |
Tool that invokes LLM sampling | |
long_task |
Tool with progress updates | |
load_bonus_tool |
Dynamically loads a new tool | |
bonus_calculator |
Calculator (dynamically loaded) | |
| Resources | info://about |
Static informational resource |
file://example.md |
File-based markdown resource | |
| Templates | greeting://{name} |
Personalized greeting |
data://items/{id} |
Data lookup by ID | |
| Prompts | greet |
Greeting in various styles |
code_review |
Code review with focus areas |
# Clone the repository
git clone https://github.com/SamMorrowDrums/mcp-csharp-starter.git
cd mcp-csharp-starter
# Restore packages
dotnet restorestdio transport (for local development):
dotnet runHTTP transport (for remote/web deployment):
dotnet run -- --http
# Or with custom port:
dotnet run -- --http --port 8080
# Server runs on http://localhost:3000 by defaultThis project includes VS Code configuration for seamless development:
- Open the project in VS Code
- The MCP configuration is in
.vscode/mcp.json - Build with
Ctrl+Shift+B(orCmd+Shift+Bon Mac) - Debug with F5 (configurations for both transports)
- Test the server using VS Code's MCP tools
- Install the Dev Containers extension
- Open command palette: "Dev Containers: Reopen in Container"
- Everything is pre-configured and ready to use!
.
βββ Program.cs # Main entry point (stdio/HTTP)
βββ Tools/
β βββ AllTools.cs # All tool definitions
βββ Resources/
β βββ AllResources.cs # All resource definitions
βββ Prompts/
β βββ AllPrompts.cs # All prompt definitions
βββ .vscode/
β βββ mcp.json # MCP server configuration
β βββ tasks.json # Build/run tasks
β βββ launch.json # Debug configurations
β βββ extensions.json
βββ .devcontainer/
β βββ devcontainer.json
βββ McpCSharpStarter.csproj
βββ global.json
βββ appsettings.json
# Development with live reload (recommended)
dotnet watch run
# Build
dotnet build
# Run tests
dotnet test
# Format code
dotnet format
# Clean
dotnet clean
# Publish for production
dotnet publish -c ReleaseThe dotnet watch run command provides automatic rebuilds during development.
Changes to any .cs file will automatically rebuild and restart the server.
The MCP Inspector is an essential development tool for testing and debugging MCP servers.
npx @modelcontextprotocol/inspector -- dotnet run -- --stdio- Tools Tab: List and invoke all registered tools with parameters
- Resources Tab: Browse and read resources and templates
- Prompts Tab: View and test prompt templates
- Logs Tab: See JSON-RPC messages between client and server
- Schema Validation: Verify tool input/output schemas
- Start Inspector before connecting your IDE/client
- Use the "Logs" tab to see exact request/response payloads
- Test tool annotations (ReadOnlyHint, etc.) are exposed correctly
- Verify progress notifications appear for
long_task - Check that McpServer injection works for sampling tools
[McpServerToolType]
public class GreetingTools
{
[McpServerTool(Name = "hello", Title = "Say Hello")]
[Description("A friendly greeting tool")]
public static string Hello(
[Description("The name to greet")] string name)
{
return $"Hello, {name}!";
}
}[McpServerResourceType]
public class StaticResources
{
[McpServerResource(
UriTemplate = "greeting://{name}",
Name = "Personalized Greeting",
MimeType = "text/plain")]
public static string Greeting(string name)
{
return $"Hello, {name}!";
}
}[McpServerTool(Name = "ask_llm")]
public static async Task<string> AskLlm(
McpServer server,
[Description("The prompt")] string prompt,
CancellationToken cancellationToken)
{
var result = await server.SampleAsync(
new CreateMessageRequestParams
{
Messages = [
new SamplingMessage
{
Role = Role.User,
Content = [new TextContentBlock { Text = prompt }]
}
],
MaxTokens = 100
},
cancellationToken: cancellationToken);
return result.Content.OfType<TextContentBlock>()
.FirstOrDefault()?.Text ?? "";
}[McpServerPromptType]
public class CodeReviewPrompts
{
[McpServerPrompt(Name = "code_review", Title = "Code Review")]
public static IEnumerable<PromptMessage> CodeReview(
[Description("The code to review")] string code,
[Description("Programming language")] string language)
{
return [
new PromptMessage
{
Role = Role.User,
Content = new TextContentBlock
{
Text = $"Review this {language} code:\n```{language}\n{code}\n```"
}
}
];
}
}Configuration via appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}Contributions welcome! Please ensure your changes maintain feature parity with other language starters.
MIT License - see LICENSE for details.