
In this article, I will explain how I built a website to generate Christmas Stories my almost 5-year-old daughter using C# and AI related technologies (GitHub Spec Kit, GitHub Copilot, Microsoft.Extensions.AI libraries).
It’s the Christmas season, so I thought it was a good time to create something related to Christmas while exploring technologies. Like all parents, I often read stories to my daughter. Usually, we pick them together at the library, but sometimes I make them up. Coming up with Christmas stories is not my strong suit so I figured I could create a web page that generates Christmas stories based on different things my daughter could choose: main character, location, object, and eventually the adventure.
This seemed a good opportunity to do Spec Driven Development with GitHub Spec Kit. I’ve already used Spec Kit in the past but it keeps evolving, and I’m interesting in getting better at using tools like this in my software development process. Spec Kit supports many coding agents, and I chose to use GitHub Copilot since it's already my daily AI coding assistant.
Using AI for development does not mean letting the AI make all the technical decisions, quite the contrary. My Christmas Story Generator was going to be composed of:
Here is what I decided to use for my stack:
I had no prior experience with Blazor, GitHub Models, or Microsoft Agent Framework, so this was truly an experiment to try out different technologies.
Microsoft.Extensions.AI which is a lower-level abstraction to directly interact with AI models, that is also used inside Microsoft Agent Framework.I created the project using the ASP.NET Core/Blazor Aspire template.

This gave me a working application with a dashboard with real-time tracking of the app.
I executed the aspire mcp init command to set up a few MCP Servers:
I manually added Microsoft Learn MCP to have access to latest version of the Microsoft documentation.
{
"servers": {
"microsoftdocs/mcp": {
"type": "http",
"url": "https://learn.microsoft.com/api/mcp",
"gallery": "https://api.mcp.github.com",
"version": "1.0.0"
},
"aspire": {
"type": "stdio",
"command": "aspire",
"args": [
"mcp",
"start"
]
},
"playwright": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@playwright/mcp@latest"
]
}
},
"inputs": []
}

Once the specify init command was executed, I had access to custom agents for each step of the spec kit process.

Since I used a template to initialize the project, I had not much to say in the constitution:
You should respect .NET best practices to code this application.
Use the MCP available to get additional information before generating code or documentation to check you have the most up to date data
The specifications were not very complex:
Implement the feature specification based on the updated constitution. I want to build a very simple website that let me select :
- the main character (who the hero will be)
- the setting (where the story takes place)
- an object or key element (that will play an important role)
- an optional action or activity
and create a Christmas story to tell to 5-years-old children
There should be 4 or 5 suggestions for each item to select and the possibility to enter something completly
The stories should last between 5 and 10 minutes
What was interesting after, was the clarify step when Copilot asked me pertinent questions with multiple choices.

For the plan, I just indicated the technical choices I made.
Create a plan for the spec. I am building with Microsoft Agent Framework, the existing template in this repository.
Use Aspire and GitHub Models while I am developping
The task step divided the work to do into multiple tasks, some of which could be parallelized.

I used the analyze step to ensure everything was consistent before starting the implementation.
My main concern was to have something functional, so I didn't focus too much on code quality. Still, I was quite pleased with what Copilot generated. The service to generate the story for instance is quite easy to understand, even for someone like me who is not familiar with how the Microsoft.Extensions.AI package works.
public StoryGeneratorService(IChatClient chatClient, ILogger<StoryGeneratorService> logger)
{
_chatClient = chatClient;
_logger = logger;
}
public async Task<GeneratedStory> GenerateStoryAsync(StoryRequest request, CancellationToken cancellationToken = default)
{
_logger.LogInformation(
"Generating story with Character={Character}, Setting={Setting}, Object={Object}, Action={Action}",
request.Character, request.Setting, request.Object, request.Action);
var userPrompt = BuildUserPrompt(request);
var messages = new List<ChatMessage>
{
new(ChatRole.System, SystemPrompt),
new(ChatRole.User, userPrompt)
};
var options = new ChatOptions
{
MaxOutputTokens = 2000,
Temperature = 0.8f
};
var response = await _chatClient.GetResponseAsync(messages, options, cancellationToken);
var content = response.Text ?? string.Empty;
var (title, storyContent) = ParseStoryResponse(content);
var story = new GeneratedStory
{
Title = title,
Content = storyContent,
ReadingTimeMinutes = GeneratedStory.CalculateReadingTime(storyContent),
GeneratedAt = DateTime.UtcNow
};
_logger.LogInformation(
"Story generated: Title={Title}, Words={WordCount}, ReadingTime={ReadingTime}min",
story.Title, storyContent.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length, story.ReadingTimeMinutes);
return story;
}
I was pretty sold on Aspire before this experiment even started, and now I’m basically all‑in. It’s so useful to improve the local developer experience of any project (regardless of the technology). And this is even more true for projects involving AI.
With less that 20 lines of code, Aspire configures and orchestrates everything the application need to work: GitHub Models, API, front end.
using Aspire.Hosting.GitHub;
var builder = DistributedApplication.CreateBuilder(args);
// Configure GitHub Models for AI story generation
var model = GitHubModel.OpenAI.OpenAIGpt4oMini;
var chat = builder.AddGitHubModel("chat", model);
var apiService = builder.AddProject<Projects.ChristmasStories_ApiService>("apiservice")
.WithReference(chat)
.WithHttpHealthCheck("/health");
var webApp = builder.AddProject<Projects.ChristmasStories_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithHttpHealthCheck("/health")
.WithReference(apiService)
.WaitFor(apiService);
builder.Build().Run();
I discovered that you can view the details of the calls to LLMs in the traces of the Aspire dashboard, which is pretty neat.

The Aspire MCP can check logs and traces, making it very handy for debugging issues. It may seem basic, but it greatly improves the coding assistant experience. It helped me understand why a story sometimes wasn't generated (the default timeout for the retry policy was too aggressive).

I was pleased with this experiment: the Christmas Story Generator is simple but works well. I haven't used it with my daughter yet because I still need to set up multiple languages, especially French. But to be honest, this experiment was mostly about me exploring a bunch of technologies.


I was pleasantly surprised to find that integrating AI into a .NET application was quite simple. Using GitHub Models was an easy way (especially with the Aspire integration) to prototype with AI models without having to create an Azure Foundry or another similar platform.
The implementation was mostly completed using GitHub Copilot in Agent mode. GitHub Spec Kit and the use of MCP Servers were very helpful in creating a solution that met my needs while respecting the technical choices I made.
It was a nice, small project that I might improve in the future. You can find the code here.
This article was published as part of the C# Advent which is a nice initiative. Make sure to check the other blog articles on the advent calendar.
Happy Holidays everyone!