Run it locally
There are two ready-made environments. local is for dotnet run: sample data on, logs to the console. docker is for Docker Compose: sample data on, logs to Seq, database on a volume, and the MCP Inspector next door. Both are for your machine. Neither is for production, which is why neither is the default.
Run it
Section titled “Run it”dotnet run --project ./src/PresentationlaunchSettings.json sets the environment to local and listens on http://localhost:5032 and https://localhost:7032. The server is at /mcp.
docker compose up -d --buildCompose builds the run stage, starts Seq and the MCP Inspector next to it, and publishes the server on http://localhost:8080/mcp. Shut it down with docker compose down, or docker compose down -v to drop the database too.
docker build . --target run --tag acme-movies:localdocker run --rm -p 8080:8080 -e DOTNET_ENVIRONMENT=docker acme-movies:localWithout Compose there’s no Seq, so the Serilog sink in appsettings.docker.json has nowhere to send the logs. Swap it for Console if you run the image on its own.
Find your way around
Section titled “Find your way around”| Where | What |
|---|---|
/mcp |
The server. Streamable HTTP, stateless, JSON-RPC in and server-sent events out |
/health/live |
Answers 200 while the process is up |
/health/ready |
Answers 200 when the database can be reached |
/.well-known/oauth-protected-resource/mcp |
Protected resource metadata, only when authentication is on |
| http://localhost:6274 | The MCP Inspector, when running with Docker Compose |
| http://localhost:5341 | Seq, when running with Docker Compose |
The health endpoints answer without a token even when authentication is on, so a load balancer never needs to log in.
Use the Inspector
Section titled “Use the Inspector”The MCP Inspector is the official test client, and Docker Compose runs it already pointed at http://api:8080/mcp. Open it, press Connect, and:
- Tools lists every tool with its description, annotations and input schema, and gives you a form to call it.
- Resources lets you read
server://versionor anymovies://,authors://orreviews://URI. - Prompts renders each prompt with its arguments, and the title and name arguments autocomplete from the database.
Running with dotnet run instead? Start the Inspector yourself with npx @modelcontextprotocol/inspector and point it at http://localhost:5032/mcp.
Try the tools from the terminal
Section titled “Try the tools from the terminal”The server is stateless, so a single tools/call works without an initialize first. The sample data uses a fixed seed, so these ids are the same on every machine. See the tool reference for every argument and result.
# One movie, with every reviewcurl -X POST "http://localhost:5032/mcp" \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"movies_get","arguments":{"movieId":"e159975e-4e6a-1ffd-713e-1eb9d8d4a593"}}}'
# Write a reviewcurl -X POST "http://localhost:5032/mcp" \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"reviews_write","arguments":{"authorId":"01fa8b58-05d2-abd4-e55c-2633222dd0a5","movieId":"e159975e-4e6a-1ffd-713e-1eb9d8d4a593","stars":5}}}'
# Read a resourcecurl -X POST "http://localhost:5032/mcp" \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":3,"method":"resources/read","params":{"uri":"server://version"}}'Run the tests
Section titled “Run the tests”dotnet testEach test class builds its own in-memory SQLite database from the migrations and the same fixed seed, and the Presentation tests talk to the server through a real McpClient from the C# SDK, so they exercise the same protocol path as any other client. No test can change what another one sees, and nothing touches movie-reviews.db.
Run just one part of the Dockerfile
Section titled “Run just one part of the Dockerfile”The Dockerfile has a stage for each job, so you can build only what you need.
| Stage | What it does |
|---|---|
build |
Restores and builds the solution |
test |
Runs every test with coverage |
coverage |
Exports the coverage report to your machine |
publish |
Publishes the Presentation project |
run |
The image you deploy |
docker build . --target coverage --output type=local,dest=outStamp a version
Section titled “Stamp a version”docker build . --target run --tag acme-movies:latest \ --build-arg VERSION_PREFIX=1.2.3 --build-arg VERSION_SUFFIX=betaThe server://version resource and the serverInfo in the initialize response then report 1.2.3-beta and the commit it was built from.