From Spec to Santa: My C#‑Powered Christmas Story Generator Experiment
Development·

From Spec to Santa: My C#‑Powered Christmas Story Generator Experiment

When AI meets bedtime stories: a C# holiday experiment

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).

Context

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:

  • a front end to let me me personalize the Christmas story (character, location, etc) and display it
  • an API to generate the story using a LLM and provide it to the front end

Here is what I decided to use for my stack:

  • Aspire to orchestrate everything locally and have a good developer experience from the start
  • C# everywhere with Blazor for the front end and an ASP.NET Minimal API for the back end
  • GitHub Models to quickly prototype with AI Models through a unified API without provisioning anything
  • Microsoft Agent Framework to create an agent that will generate the Christmas stories

I had no prior experience with Blazor, GitHub Models, or Microsoft Agent Framework, so this was truly an experiment to try out different technologies.

During implementation, I realized Microsoft Agent Framework is focused on building AI agents and multi-agent workflows, which is overkill for my needs. So I used Microsoft.Extensions.AI which is a lower-level abstraction to directly interact with AI models, that is also used inside Microsoft Agent Framework.

Set up the project and development environment

I created the project using the ASP.NET Core/Blazor Aspire template.

Aspire dashboard with front end and api.

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:

  • Playwright MCP to let my coding assistant interact with my app through the browser
  • Aspire MCP to easily debug my application with access to logs, traces, metrics, etc

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": []
}

The GitHub Spec Kit journey

Specify init command in the terminal.

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

Spec Kit custom agents in VS Code.

Since I used a template to initialize the project, I had not much to say in the constitution:

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:

Specify
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.

GitHub Copilot asking question during the clarify step.

For the plan, I just indicated the technical choices I made.

Plan
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.

Screenshot of the tasks document.

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.

StoryGeneratorService.cs
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;
}

Aspire awesomeness

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.

AppHost.cs
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.

Aspire's Gen AI Visualizer in the traces view of the Aspire dashboard.

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).

Prompt using the Aspire MCP in the GitHub Copilot chat.

Results and Final Thoughts

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.

A Christmas Story Generator interface featuring options to select a main character, setting, key object, and optional adventure.

Screenshot of a webpage titled "Christmas Story Generator" with a story about a penguin named Pip.

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!

The opinions expressed herein are my own and do not represent those of my employer or any other third-party views in any way.

Copyright © 2026 Alexandre Nédélec. All rights reserved.