Skip to content

fodecoder/mcp-server-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mcp-server-example

A demonstration MCP (Model Context Protocol) server in C#/.NET, exposing tools and resources over the Streamable HTTP transport. Built as a practice project — see PLAN.md for the full design rationale, security checklist, and open decisions.

Stack

Project layout

src/
  McpServerExample.Server/                 ASP.NET Core host, MCP tools/resources, validation
  McpServerExample.Clients.JsonPlaceholder/ Typed REST client for jsonplaceholder.typicode.com
  McpServerExample.Clients.Countries/      Typed GraphQL client for countries.trevorblades.com
tests/
  McpServerExample.Server.Tests/           xUnit tests (validation, snapshot, integration)

Running the server

dotnet run --project src/McpServerExample.Server

The server listens on http://localhost:5048 (see Properties/launchSettings.json) and exposes the MCP endpoint at POST /mcp (Streamable HTTP transport, per the MCP spec). No authentication; intended for localhost use only.

Tools (6)

Tool Source Notes
get_post JSONPlaceholder (REST) Read-only
list_posts_by_user JSONPlaceholder (REST) Read-only
get_user JSONPlaceholder (REST) Read-only
create_post JSONPlaceholder (REST) Simulated write — JSONPlaceholder doesn't persist it
get_country Countries (GraphQL) Read-only
list_countries_by_continent Countries (GraphQL) Read-only

Every tool argument is validated twice: declaratively (JSON Schema constraints — range, pattern, length, enum, additionalProperties: false) and imperatively (guard clauses in Validation/Guards.cs), because the SDK does not enforce the declared schema at runtime — it only publishes it. See PLAN.md §5 for the full rationale.

Resources (3)

URI MIME type Content
demo://catalog/products application/json Fictional ~20-product catalog
demo://docs/product-manual application/pdf Synthetic 2-3 page manual (blob), generated at first access via QuestPDF
demo://docs/product-manual/text text/plain Same manual as plain text

Resources are served from a fixed set of [McpServerResource] methods with static URIs — there is no dynamic path composition, so an unknown URI is rejected before any file/asset lookup happens.

Logging

Logging is handled by Serilog, fully configured from the Serilog section in appsettings.json / appsettings.Development.json (sinks, minimum levels, enrichers, output templates — no logging setup lives in code beyond wiring UseSerilog() up in Program.cs).

  • Console: colored, human-readable single-line-per-entry output.
  • File: logs/mcpserverexample-.log, rolling daily, 14 days retained, enriched with the application name, environment, machine name, process ID, and thread ID on every line.
  • Tool arguments are logged at Debug (never full response bodies — see PLAN.md §5); guard-clause rejections at Warning; the one write tool (create_post) and one-time PDF generation at Information; unexpected upstream failures at Error.
  • appsettings.Development.json bumps the server's own namespace to Verbose for local debugging.

Deviation from PLAN.md

The plan proposed StrawberryShake for the GraphQL client (§4). At implementation time, StrawberryShake.Server was found stuck at v12.15.3 while the underlying HotChocolate ecosystem is at v16.x — a strong signal the codegen tooling is unmaintained relative to current .NET tooling. Instead, McpServerExample.Clients.Countries uses a small hand-written typed HTTP client with the two GraphQL queries committed as .graphql files (embedded resources) and typed variables — this preserves the plan's actual goal (precompiled queries, no model-supplied query fragments, no GraphQL injection surface) without the fragile dependency.

Tests

dotnet test

26 tests covering:

  • ToolValidationTests — hostile input (out-of-range, wrong type, injection-shaped strings) against the real guard clauses.
  • ToolDefinitionSnapshotTests — golden-file test over tools/list and resources/list output (Snapshots/*.snapshot.json). Any change to a tool/resource's name, description, schema, or annotations fails the build until the snapshot is updated in an explicit, reviewable commit — this is the anti-rug-pull mechanism from PLAN.md §6.
  • McpIntegrationTests — end-to-end calls through an in-process WebApplicationFactory<Program> + a real McpClient, exercising every tool and resource (hits the real JSONPlaceholder/Countries APIs over the network).

Manual smoke test with MCP Inspector

npx @modelcontextprotocol/inspector --cli http://localhost:5048/mcp --method tools/list
npx @modelcontextprotocol/inspector --cli http://localhost:5048/mcp --method tools/call --tool-name get_country --tool-arg countryCode=IT
npx @modelcontextprotocol/inspector --cli http://localhost:5048/mcp --method resources/read --uri demo://catalog/products

(Requires the server to be running separately — dotnet run --project src/McpServerExample.Server.)

About

MCP server for practice porpuses

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages