Code4mk.McpServer.Template 1.0.0

dotnet new install Code4mk.McpServer.Template@1.0.0
                    
This package contains a .NET Template Package you can call from the shell/command line.

.NET MCP Server Template

NuGet Downloads template-ci License: MIT

A production-ready Model Context Protocol server for ASP.NET Core in one command. Sign-in with any identity provider, interactive UIs inside the chat, typed API clients, tests, Docker and docs: the parts that take weeks are already done, so you write tools.

dotnet new install Code4mk.McpServer.Template
dotnet new code4mk-mcp -n Acme.Mcp -o acme-mcp

Built on the official MCP C# SDK 2.0 and .NET 10.

Why this template

Remote MCP servers need real OAuth, and that's the hard part. MCP clients such as Claude expect OAuth 2.1 with dynamic client registration, PKCE, and protected resource metadata. Most identity providers (Entra ID, Google, Okta, ...) don't offer dynamic registration. This template ships an OIDC proxy that makes any OIDC provider work with three settings, including the consent screen, encrypted token storage and Redis for multiple instances. Users sign in once: expired tokens renew silently with rotating refresh tokens, and sign-ins survive restarts and deploys.

Tools can show real UI, not just text. MCP Apps are wired end to end: React + Tailwind views that follow the host's theme, call your tools themselves (paging, drill-down), validate every response, and preview with sample data without a server.

The structure stays clean as it grows. Tools → services → integrations, one direction, enforced by architecture tests: the build fails if a tool skips auth, calls an API directly, or leaks external models.

Errors the model can fix. Every tool and prompt argument is validated before your code runs; the model gets a list like request.durationDays: High-priority projects must be 90 days or less. and retries correctly.

MCP App: projects dashboard with drill-down Consent page shown before a new MCP client connects
An MCP App: the tool's result as a dashboard; selecting an owner calls another tool from the view Consent before a new MCP client connects, then sign-in at your identity provider

Quick start

dotnet new install Code4mk.McpServer.Template
dotnet new code4mk-mcp -n Acme.Mcp -o acme-mcp
cd acme-mcp
cp .env.example .env                   # set MCP_AUTH_MODE=none for a first run without an identity provider
dotnet watch --project src/Acme.Mcp    # → Server running at http://localhost:5080
npx @modelcontextprotocol/inspector    # Streamable HTTP → http://localhost:5080/mcp

-n is the .NET name (PascalCase), -o the folder. Namespaces, the solution, Docker names and GUIDs follow it.

Connect a real identity provider: register one web app with redirect URI http://localhost:5080/oauth/callback, then set OIDC_DISCOVERY_URL, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET in .env and MCP_AUTH_MODE=required. Add the server in Claude (Settings → Connectors → Add custom connector) and it opens your login page.

What you get

Area Included
MCP Tools, resources and prompts discovered from attributes; stateless Streamable HTTP (scales horizontally); annotations; structured content with output schemas
Auth OIDC proxy for any OIDC provider (Entra ID, Google, Auth0, Okta, Keycloak, Zitadel, ...): DCR, PKCE, consent, ID token + userinfo merged into one AppUser. Any MCP client can connect, no allow-list. Or the jwt provider, or your own IAuthProvider. [Authorize] per item, scope policies, any IdP claim when you need it. AppUser injectable everywhere; ISignInHandler calls your backend at sign-in (sync users, permissions, deny); ITokenClaimsEnricher shapes the token's claims
MCP Apps ui/ workspace (React 19, Vite, Tailwind 4, ext-apps 2): one bundle per view, host theme, tool calls from the view, zod-validated outputs, sandbox preview, per-entry builds
Validation Data annotations and IValidatableObject on every tool and prompt call, nested objects included
Integrations Typed API clients from one line of registration; retries, circuit breaker, timeouts; bearer, api_key, basic, client_credentials, or the signed-in user's token
Redis Optional, for scale-out or your own caching: one shared StackExchange.Redis connection, REDIS_URL as URL or connection string (cluster, Sentinel, TLS), and one IRedisConnectionFactory to plug in any vendor (Azure Entra ID, AWS, GCP, certificates). Health-checked
Settings .env plus typed settings classes validated at startup, every problem listed by variable name
Dev loop dotnet watch with hot reload, pnpm run watch for views, dual-stack localhost, startup URL in the log
Tests Unit, integration (a real MCP client against the in-memory server, including the full OAuth flow against a fake IdP) and architecture rules: 96 tests out of the box
Ship Multi-stage Dockerfile, Compose, GitHub Actions (build, test, UI bundle check, Docker), pinned SDK, central package versions
Docs 12 guides organized by task, 4 architecture decision records

Project layout

acme-mcp/
├── src/Acme.Mcp/
│   ├── Capabilities/       Tools/, Resources/, Prompts/: what the server exposes (thin)
│   ├── Services/           Business logic
│   ├── Models/             Request / response DTOs
│   ├── Integrations/       One folder per external API
│   ├── Core/               Auth, MCP setup and filters, validation, settings, errors: plumbing you rarely touch
│   └── ui_dist/            Built MCP App bundles
├── ui/                     MCP App views (React + Vite + Tailwind)
├── tests/                  Unit, Integration, Architecture (mirror src/)
├── docs/                   Guides and ADRs
└── docker/                 Dockerfile, Compose

Adding a tool is one class; there is no registration line:

[McpServerToolType]
[Authorize]
public sealed class InvoiceTools(IInvoiceService invoices)
{
    [McpServerTool(Name = "get_invoice", ReadOnly = true, UseStructuredContent = true)]
    [Description("Gets an invoice by number.")]
    public Task<InvoiceDto> GetInvoice(
        [Required, RegularExpression("^INV-[0-9]{6}$")] string number,
        AppUser user,
        CancellationToken cancellationToken) =>
        invoices.GetAsync(number, user, cancellationToken);
}

Works with

  • Clients: Claude (claude.ai and Desktop), VS Code, Cursor, MCP Inspector, and any client that speaks Streamable HTTP with OAuth.
  • Identity providers: anything with an OpenID Connect discovery URL.
  • Hosting: anywhere a container runs; set APP_URL behind a reverse proxy.

Documentation

Each generated project includes its docs. Browse them on GitHub:

Requirements: .NET 10 SDK. Optional: Docker; Node 22+ and pnpm for MCP App views.

Contributing

Issues and pull requests are welcome. To work on the template itself:

git clone https://github.com/code4mk/dotnet-mcp-server-template
dotnet new install ./dotnet-mcp-server-template/templates/DotnetMcpTemplate --force
dotnet new code4mk-mcp -n Sample.Mcp -o ../sample-mcp     # always create projects outside the repo
  • DotnetMcpTemplate (and dotnetmcptemplate-slug) are placeholders; keep them. Package versions live in templates/DotnetMcpTemplate/Directory.Packages.props.
  • CI creates a project from the template, builds and tests it, checks the UI bundles, and packs the template.
  • Changing, testing and publishing the template: nuget-publish.md.

License

MIT © Code4mk

  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 77 9/26/2026