%%
> Published `=dateformat(this.created, "MMM dd, yyyy")`
%%
> Published Jun 06, 2025
# Having Fun Building a Simple TODO .NET MCP Server
I was recently playing around building MCP Server and decided to take my basic .NET Blazor in-memory TODO list app and supercharge it by turning it into a .NET MCP (Model Context Protocol) server. The goal? To make it smart enough to talk to AI tools like Claude Desktop or GitHub Copilot, so I can manage my tasks using natural language prompts. In this blog I explained exactly how I did it.
## Starting Point: A Simple Blazor TODO App
The original app was a straightforward Blazor Server application that allowed me to:
- Add and remove tasks
- Mark tasks as complete or incomplete
- View the list of tasks
It was all in-memory, no database involved, and hosted via Docker for easy setup.
## Enhancing the App with MCP
To enable AI assistants to interact with the app, I needed to set up an MCP server. Here's how I integrated it:
### 1. Creating MCP Builder
To add MCP functionality, I created a separate MCP web application within my existing Blazor app:
```cs
var mcpBuilder = WebApplication.CreateBuilder();
mcpBuilder.WebHost.ConfigureKestrel(options => options.ListenAnyIP(5051));
mcpBuilder.Services.AddSingleton(todoService);
var mcpApp = mcpBuilder.Build();
```
This snippet sets up a new application on port `5051` specifically to handle MCP requests.
### 2. Adding MCP Endpoints
I defined endpoints to handle specific actions (tools):
- **Add a TODO item:**
```cs
mcpApp.MapPost("/mcp/add", (TodoItem item) => {
todoService.Add(item);
return Results.Ok(todoService.GetAll());
});
```
- **Remove a TODO item:**
```cs
mcpApp.MapPost("/mcp/remove", (TodoItem item) => {
todoService.Remove(item.Title);
return Results.Ok(todoService.GetAll());
});
```
- **Mark a TODO item as done:**
```cs
mcpApp.MapPost("/mcp/markdone", (TodoItem item) => {
todoService.MarkAsDone(item.Title, item.IsDone);
return Results.Ok(todoService.GetAll());
});
```
- **List all TODO items:**
```cs
mcpApp.MapGet("/mcp/todos", () => {
var todos = todoService.GetAll();
return Results.Ok(new {
success = true,
todos,
count = todos.Count
});
});
```
### 3. Keep-Alive Endpoint
I also added a keep-alive endpoint to ensure persistent connections:
```cs
mcpApp.MapGet("/mcp/", async (HttpContext context) => {
context.Response.Headers["Content-Type"] = "text/event-stream";
await context.Response.Body.FlushAsync();
while (!context.RequestAborted.IsCancellationRequested)
{
await context.Response.WriteAsync(": keep-alive\n\n");
await context.Response.Body.FlushAsync();
await Task.Delay(10000, context.RequestAborted);
}
});
```
### 4. Central MCP Endpoint
To support flexible MCP commands, I created a central JSON-RPC style endpoint:
```cs
mcpApp.MapPost("/mcp", async (HttpContext context) => {
// JSON-RPC processing logic for handling dynamic tool calls (add, remove, markdone, get)
// See repository for complete implementation
});
```
This endpoint dynamically handles requests and provides capabilities to initialize the server and list available tools.
### 5. Running the MCP Server
To run the MCP server alongside the main app, I launched it in a separate task:
```cs
_ = Task.Run(async () => await mcpApp.RunAsync());
```
### 6. Integrating AI Tools
I then configured VS Code to interact with my MCP server by adding a `mcp.json` file:
```json
{
"servers": {
"my-mcp-server-635d5e26": {
"url": "http://localhost:5051/mcp"
}
}
}
```
## Running the App with Docker
The app is Dockerized for easy setup. To run it locally:
1. Clone the repository:
```
git clone https://github.com/pakbaz/TodoList.git
cd TodoList
```
2. Build and run the Docker container:
```
docker build -t todolist-app .
docker run -p 8080:80 -p 5051:5051 todolist-app
```
The Blazor web interface will be available at `http://localhost:8080`, and the MCP server will listen at `http://localhost:5051/mcp`.
## Using AI to Manage the TODO List
With the MCP server in place, I can now use natural language prompts to manage my tasks. For example:
> "Fetch the latest 15-inch MacBook Air from apple.com and add the name and model number to the TODO list with a 'Buy' prefix."
An AI assistant can process this prompt by:
1. Fetching the latest MacBook Air information from Apple's website.
2. Constructing an MCP command:
```json
{
"method": "add",
"params": {
"item": "Buy MacBook Air 15-inch M4 (Model Mac15,13)"
}
}
```
3. Sending the command to the MCP server at `http://localhost:5051/mcp`.
The server processes the command and updates the TODO list accordingly.
## Conclusion
By integrating an MCP server into my Blazor TODO app, I've enabled seamless interaction with AI tools, allowing for dynamic task management through natural language. It's a simple yet powerful enhancement that bridges traditional applications with modern AI capabilities.
Feel free to explore the project on [GitHub](https://github.com/pakbaz/TodoList) and try it out yourself!