Use AI to build .NET faster
The fastest wins in the whole roadmap: make Claude Code write your .NET, then turn it into a specialist with skills and agents. Real code, no theory.
Make Claude Code write your .NET
By the end of this lesson you'll have an AI assistant that writes .NET the way you write it - matching your stack, architecture, and conventions on the first try. Most developers get this wrong.
The problem
You ask Claude Code for one endpoint. You get back a repository interface, an AutoMapper profile, DateTime.Now, and a folder structure from someone else's project. The code isn't wrong - it's just not your code.
The key insight: it's not that the AI doesn't know .NET. It starts every session knowing nothing about your repo, so it fills the gap with the most common .NET code it has seen. The fix isn't a better prompt every time - it's giving the AI context, once.
Step 1 - Install Claude Code
# install, then run inside any project folder
npm install -g @anthropic-ai/claude-code
claudeThis lesson uses Claude Code, but the CLAUDE.md idea maps to Cursor's rules and Copilot's instructions too. Open it inside a real solution - you learn this on code you care about.
Step 2 - The one file that changes everything: CLAUDE.md
CLAUDE.md is a markdown file at your repo root. Claude Code loads it automatically at the start of every session, before your first prompt. Think of it as the briefing you'd give a new senior dev joining your team. It answers four questions:
- What's the stack? (exact versions)
- How is the project structured? (architecture + folders)
- What are the conventions? (the patterns you use)
- What should it never do? (the patterns you don't use)
# Project: Orders API
## Stack
- .NET 10 / C# 14 · ASP.NET Core Minimal APIs
- EF Core 10 + PostgreSQL · FluentValidation · xUnit + Testcontainers
## Architecture
- Vertical Slice: everything for one feature in Features/[Feature]/.
## Conventions
- Result pattern, not exceptions for control flow.
- Inject TimeProvider - never DateTime.Now.
- TypedResults; DTOs are records; never expose EF entities.
- CancellationToken on every async method.
## Never suggest
- AutoMapper - write explicit mappings.
- Repository/UnitOfWork over EF Core - DbContext already is one.
- Exceptions for expected outcomes (not-found, validation).
- In-memory database in tests - use Testcontainers.Step 3 - Prompt for real work
- Describe the outcome, not the steps. Not "make a controller + service + repository" - but "add a create-product endpoint, return a Result, follow the existing features."
- Ask for a plan before edits on anything non-trivial: "give me a short plan and wait for my OK." Catches wrong assumptions before they become wrong code.
- Tell it to leave good code alone: "Don't invent problems."
Worked example
public class OrdersController : ControllerBase
{
private readonly IOrderRepository _repo;
[HttpPost] public async Task<IActionResult>
Create(CreateOrderDto dto){
var o = new Order{ CreatedAt = DateTime.Now };
await _repo.AddAsync(o);
return Ok(o); // leaks the entity
}
}public sealed class CreateOrderEndpoint : IEndpointGroup
{
public void Map(IEndpointRouteBuilder app) =>
app.MapPost("/api/orders", Handle)
.Produces<OrderResponse>(201)
.ProducesValidationProblem();
static async Task<Results<Created<OrderResponse>,
ValidationProblem>> Handle(CreateOrderRequest req,
IOrderService svc, TimeProvider time,
CancellationToken ct) => ...
}Same prompt. Different universe. That's the CLAUDE.md at work.
Where CLAUDE.md lives (and its friends)
They stack - so you can set global preferences once and override per project:
Use Plan Mode for anything risky
Claude Code has a plan mode: it proposes a full plan and waits for your approval before touching a single file. Use it for migrations, refactors, or anything that spans several files. It's the difference between "AI did something to my repo" and "AI did exactly what I approved."
Prompting cheat sheet
The pattern: state the outcome, name the constraints, and say what to avoid.
The bad defaults → what they should become
These are the patterns your CLAUDE.md should steer toward - and the raw material for a sharp "Never suggest" section.
- No CLAUDE.md at all → generic output → you blame the AI. The #1 mistake.
- A CLAUDE.md that's docs for humans, not instructions for the AI. Keep it concise and imperative.
- Accepting code you don't understand - you're still responsible.
- Skipping the "Never suggest" section - the highest-value part.
- Add a
CLAUDE.mdto a real project. - Fill in "Never suggest" with the 3 patterns you're tired of correcting.
- Do one backlog task fully with Claude Code - plan first, then edits.
- Post what changed in the community feed.
CLAUDE.md fixes that. Cover stack, architecture, conventions, and (most importantly) what to never suggest. Prompt for outcomes, ask for a plan, leave good code alone.Next → Part 2: make the assistant a specialist with skills and agents.
Turn the assistant into a .NET specialist
In Part 1 you gave Claude context. Now, instead of re-explaining a task every time, you give it reusable capabilities it loads automatically. That's what skills and agents are.
Skill vs. agent - the 20-second version
- A skill is a focused capability Claude loads automatically when your request matches ("optimize this EF query", "scaffold an endpoint"). You don't call it - you describe what you want.
- An agent explores your codebase on its own and produces a report ("audit the security of this API"). You invoke it by asking for what it does.
Step 1 - Install a set of skills
/plugin marketplace add StefanTheCode/dotnet-ai-toolkit
/plugin install dotnet-ai-toolkit@thecodeman-ai-toolkitThen just talk to Claude Code normally and the matching skill kicks in - no command to remember:
> This EF query is slow, optimize it
> Scaffold a products endpoint
> Write integration tests for the checkout flow
> Review this PR for .NET antipatternsEach skill carries a fixed checklist, so output is consistent instead of depending on how you phrased the prompt. Start with the 5 you'll use daily - don't memorize 44.
Where skills & agents live
Reopen the session after adding a skill so it loads. Update marketplace skills anytime with /plugin marketplace update thecodeman-ai-toolkit.
Step 2 - How a skill works (so you can write one)
A skill is a folder with a SKILL.md: YAML frontmatter (the trigger) + instructions (what to do).
---
name: ef-core-query-optimizer
description: Optimize EF Core queries. Use whenever the user shares
EF Core / LINQ code or mentions N+1, AsNoTracking, projections...
---
# EF Core Query Optimizer
Run this checklist in order:
1. Projection - .Select() to a DTO if only some columns are needed
2. Tracking - .AsNoTracking() for read-only queries
3. N+1 - detect lazy loading in loops
4. Async + CancellationToken on every query
Output: the rewritten query + one line per change. Leave optimal code alone.- The
descriptionis the trigger. Make it specific and slightly pushy about when to fire. Vague description → skill never triggers. - The body is a checklist, not an essay. Concrete steps + BAD/GOOD examples + a fixed output format = consistent results.
Anatomy of a description that actually triggers
Name the triggers (the words and situations), list synonyms, and be a little pushy ("Always use this for…"). A references/ folder can hold extra material that loads only when the skill is active - keeping your main context light.
Step 3 - Write your own skill
Pick one task you repeat weekly. Create .claude/skills/my-endpoint/SKILL.md:
---
name: my-endpoint
description: Scaffold a new Minimal API endpoint in our style. Use when
the user wants to add an endpoint, a route, or a new feature slice.
---
# New Endpoint
Create a full vertical slice in Features/[Feature]/:
- Request/Response records (never expose EF entities)
- FluentValidation validator + endpoint filter
- Handler returning Result<T>
- IEndpointGroup with TypedResults + OpenAPI metadata
- CancellationToken threaded through
- One integration test (WebApplicationFactory + Testcontainers)Reopen the session, type "add a create-customer endpoint" - your skill fires. You just encoded your team's standard once, forever.
Step 4 - Use an agent
When you want a review of the whole codebase, ask for what the agent does:
> Audit the security of this API
> Review this PR like a senior .NET engineer
> Find the architecture problems in this solutionThe agent explores your code on its own and returns a ranked report - Critical / Should-fix / Nit - with a fix for each. The fastest second pair of eyes you'll ever have.
An agent is a single .md file with frontmatter that defines its role, the tools it may use, and (optionally) the model:
---
name: aspnetcore-security-auditor
description: Audits an ASP.NET Core codebase against the OWASP Top 10 and
.NET-specific risks. Use for a security review or "is my API secure".
tools: Read, Glob, Grep, Bash
model: inherit
---
# ASP.NET Core Security Auditor
Walk the endpoints. Check authorization, injection, secrets in source,
CORS, mass assignment, vulnerable dependencies.
Output a ranked report (Critical / Should-fix / Nit) with a fix for each.Level up - slash commands & hooks
Slash commands wrap a whole workflow behind one command. Instead of describing the steps, you type /scaffold and it runs the right skills and agents in order. A command is just a markdown file in .claude/commands/:
# .claude/commands/scaffold.md
Scaffold a complete vertical-slice feature: endpoint, validation,
Result handling, OpenAPI metadata, CancellationToken, and one
integration test. Match the existing features. Then run the tests.Handy commands to build: /scaffold, /verify, /code-review, /security-scan.
Hooks run automatically around tool use - shell commands wired to events:
Hooks turn your conventions from "things you hope the AI follows" into "things the tooling enforces."
- Fix the
descriptionfirst - 90% of the time that's it. - Reopen the session after adding a skill - it loads at start.
- Name it explicitly in your prompt ("optimize this EF Core query").
- Check the folder - a skill is
skills/<name>/SKILL.md, not a loose.md.
- Install the ToolKit skills and use 5 on a real repo.
- Write one custom skill for a task you repeat weekly.
- Run one agent (security or code review) and act on its top finding.
- Share your custom skill (or what the agent caught) in the feed.
description is the trigger, the body is a checklist). Agents = specialists that review your whole codebase. Together they turn a general assistant into your .NET specialist.Next → Part 3: MCP - give the AI real tools in C#, so it can do things in your systems, not just write code.
Give the AI real tools: MCP in C#
So far the AI only writes code. In this lesson you give it tools - the ability to actually do things in your systems: query a database, call your API, check a ticket. You build the tool once, in C#, and any AI client can call it.
What MCP is (in one paragraph)
MCP - the Model Context Protocol - is a standard way for AI clients (Claude Code, Copilot, Cursor) to call external tools. You write an MCP server: a small program that exposes some operations as tools. The client tells the model which tools exist, the model picks one when it needs it, your C# runs, and the result flows back into the conversation. Skills tell the AI how to write code; MCP tools let it do something.
How a tool call actually flows
- Your server tells the client which tools exist - name, description, parameters.
- You ask a question; the model decides a tool would help and picks one.
- The client calls your C# method with the arguments the model filled in.
- Your code runs - a real query, a real API call - and returns a result.
- The model reads that result and answers, grounded in what your tool returned.
You never wire the call up by hand - the model decides, the client routes, your C# runs. Your whole job is to write good tools and describe them well.
When you actually want a tool
Step 1 - Create the server
dotnet new console -n OrdersMcp
cd OrdersMcp
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.HostingThe C# SDK is ModelContextProtocol. It still ships under --prerelease - drop the flag once your version is stable.
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
var builder = Host.CreateApplicationBuilder(args);
builder.Services
.AddMcpServer() // register the server
.WithStdioServerTransport() // the client launches it, talks over stdio
.WithToolsFromAssembly(); // auto-discover [McpServerTool] methods
// your own services are available to tools via DI
builder.Services.AddSingleton<IOrderService, OrderService>();
await builder.Build().RunAsync();Three lines do the work: AddMcpServer registers it, WithStdioServerTransport lets the client launch it, and WithToolsFromAssembly auto-discovers your tools.
Step 2 - Write a tool
A tool is just a method with two attributes. The [Description] is what the model reads to decide when to call it - treat it like a skill's trigger. Services are injected from DI, so your tool can use the same IOrderService your app already has.
using System.ComponentModel;
using ModelContextProtocol.Server;
[McpServerToolType]
public sealed class OrderTools
{
// The model reads this Description to decide WHEN to call the tool.
[McpServerTool, Description("Get the current status of an order by its id.")]
public static async Task<string> GetOrderStatus(
IOrderService orders, // injected from DI
[Description("The order id, e.g. 1024")] int orderId,
CancellationToken ct)
{
var order = await orders.FindAsync(orderId, ct);
return order is null
? $"No order {orderId} found."
: $"Order {orderId}: {order.Status}, total {order.Total:C}.";
}
}Step 3 - Connect it to Claude Code
# Claude Code: register the server (run from any project)
claude mcp add orders -- dotnet run --project ./OrdersMcp
# then just ask - Claude calls the tool itself
> what's the status of order 1024?Prefer config in the repo? Drop a .mcp.json so the whole team gets the same tools:
{
"mcpServers": {
"orders": {
"command": "dotnet",
"args": ["run", "--project", "./OrdersMcp"]
}
}
}stdio vs. HTTP - which transport?
Start with stdio. Move to HTTP once a server needs to be shared, hosted, or consumed by more than a command-line client.
See it in a real project: Performance Lab
Here's a full MCP server I built so you can read real code, not just snippets. Performance Lab lets you ask Copilot or Claude - in plain English - to load-test a .NET API, spot ThreadPool starvation, GC pressure, or a high error rate, and suggest the fix. It ships with a sample API full of intentionally broken endpoints and a Blazor dashboard to visualise the runs.
Because a Blazor dashboard and AI clients both talk to it, the server uses the HTTP transport - the exact case the table above calls out:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMcpServer()
.WithHttpTransport() // remote server - clients connect over HTTP
.WithToolsFromAssembly();
var app = builder.Build();
app.MapMcp("/mcp"); // the MCP endpoint Copilot/Claude connect to
app.Run();// .vscode/mcp.json (GitHub Copilot)
{
"servers": {
"performance-lab": {
"type": "http",
"url": "http://localhost:5200/mcp"
}
}
}The tools are a class with a primary constructor - your services (LoadTestRunner, ResultAnalyzer, a store, a logger) are injected straight in, exactly like anywhere else in ASP.NET Core:
[McpServerToolType]
public sealed class PerformanceTools(
LoadTestRunner runner,
ResultAnalyzer analyzer,
IResultStore store,
ILogger<PerformanceTools> logger) // all injected from DI
{
[McpServerTool(Name = "run_load_test")]
[Description(
"Run a load test against an API endpoint. Fires concurrent HTTP " +
"requests and returns throughput, latency percentiles, and error " +
"rate. Returns a result ID for analyze_results or generate_report.")]
public async Task<string> RunLoadTest(
[Description("Full URL, e.g. http://localhost:5100/fast")] string url,
[Description("Seconds to run. Default: 10")] int durationSeconds = 10,
[Description("Concurrent virtual users. Default: 10")] int concurrentUsers = 10,
CancellationToken ct = default)
{
var result = await runner.RunAsync(
new LoadTestRequest { Url = url, DurationSeconds = durationSeconds,
ConcurrentUsers = concurrentUsers }, ct);
var analysis = analyzer.Analyze(result); // ThreadPool starvation? GC pressure?
store.Add(result, analysis);
return FormatLoadTestResult(result, analysis); // a tight, model-readable summary
}
}/slow vs /fast. You'll watch the AI diagnose your API from real numbers.- A vague
[Description]→ the model never calls the tool (same rule as skills). - Returning a giant blob - return a tight, readable result the model can use.
- Logging to stdout on a stdio server - it corrupts the protocol. Log to stderr.
- Read/write tools with no guardrails. Start read-only.
- Build a one-tool MCP server over stdio (start read-only - a lookup).
- Register it with
claude mcp addand call it from a prompt. - Inject a real service so it returns real data from your app.
- Share what tool you exposed in the community feed.
[McpServerTool] + a sharp [Description], run it over stdio, and register it with one command. Keep tools scoped and safe - they run for real.Next → Part 4: stop using other people's AI - build your own AI feature in C#.
Build your first AI feature
Parts 1-3 were about using AI to build .NET. Now you flip sides: you put AI inside your app. The good news - it's just another service you inject. One interface, IChatClient, and any provider behind it.
The one abstraction: IChatClient
Microsoft.Extensions.AI gives .NET a single interface for talking to any LLM - IChatClient. You register one provider at startup; the rest of your code depends on the interface, not on OpenAI or Azure. Swap providers by changing one line.
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Extensions.AI.OpenAIusing Microsoft.Extensions.AI;
using OpenAI.Chat;
// One registration. The rest of your app depends on IChatClient, not OpenAI.
builder.Services.AddChatClient(
new ChatClient("gpt-4o-mini", builder.Configuration["OpenAI:Key"])
.AsIChatClient());The model id lives on the ChatClient; .AsIChatClient() adapts it to the standard interface. (The exact adapter name tracks the package version.)
Step 1 - Call the model
Inject IChatClient like any other service and ask:
public sealed class SummaryService(IChatClient chat)
{
public async Task<string> SummarizeAsync(string text, CancellationToken ct)
{
var response = await chat.GetResponseAsync(
$"Summarize this in exactly two sentences:\n\n{text}",
cancellationToken: ct);
return response.Text;
}
}Step 2 - Stream the response
For anything user-facing, stream tokens as they come instead of waiting for the whole answer:
// Stream tokens as they arrive - perfect for chat UIs.
await foreach (var update in
chat.GetStreamingResponseAsync(prompt, cancellationToken: ct))
{
Console.Write(update.Text);
}Step 3 - Get structured output (the real unlock)
Free text is hard to use in code. Ask for a typed result and the library builds the JSON schema, tells the model, and parses the response into your record:
public record SupportTicket(string Title, string Priority, string[] Tags);
// Ask for a typed result - the library builds the schema and parses the JSON.
var response = await chat.GetResponseAsync<SupportTicket>(
$"Turn this email into a support ticket:\n\n{email}",
cancellationToken: ct);
SupportTicket ticket = response.Result; // strongly typed, ready to saveThis is what turns an LLM from a chatbot into a feature: classify a ticket, extract fields from an email, tag content - all as real C# objects.
Give it a role - and memory
A single prompt is stateless. Pass a list of messages instead - a System message sets the role and rules, and you keep appending turns so the model remembers the conversation:
List<ChatMessage> chat =
[
new(ChatRole.System, "You are a terse .NET assistant. Answer in one paragraph."),
new(ChatRole.User, userQuestion),
];
var response = await chat_client.GetResponseAsync(chat, cancellationToken: ct);
chat.AddMessages(response); // keep the reply so the next turn has contextThe System message is where you put guardrails - tone, format, "only answer from the context I give you." It's the CLAUDE.md of your feature.
Let the model call your code (tools)
Same idea as MCP from Part 3 - but in-process. Hand the model a C# method and it decides when to call it, so your feature can pull real data mid-answer instead of guessing:
// Expose a plain C# method as a tool the model can call.
[Description("Get the current stock count for a product SKU.")]
static int GetStock(string sku) => Inventory.CountFor(sku);
var options = new ChatOptions
{
Tools = [AIFunctionFactory.Create(GetStock)]
};
// With .UseFunctionInvocation() on the pipeline, the library runs the
// call for you and feeds the result back to the model - automatically.
var response = await chat.GetResponseAsync(
"How many units of SKU-42 are left?", options, cancellationToken: ct);MCP tools do this across processes for any client; AIFunctionFactory does it inside one app. This is the seed of an AI agent (Step 7 in the roadmap).
Swap providers without touching your code
Develop locally against Ollama (free, private), ship on OpenAI or Azure. Your feature code never changes.
Production niceties, almost for free
IChatClient is a pipeline - wrap it with middleware the same way you'd wrap an HTTP client:
builder.Services
.AddChatClient(new ChatClient("gpt-4o-mini", key).AsIChatClient())
.UseFunctionInvocation() // let the model call your C# tools
.UseOpenTelemetry() // traces + token metrics
.UseLogging(); // every call logged- Use the smallest model that works (like
gpt-4o-mini) - upgrade only when quality demands it. - Cache identical prompts with
.UseDistributedCache(). - Keep prompts tight and cap the output length - you pay for both directions.
- Never put a secret key in source - use configuration / user-secrets.
- Register an
IChatClientand call it from one endpoint. - Return a typed result with
GetResponseAsync<T>. - Point it at Ollama locally, then at OpenAI - same code.
- Add
.UseLogging()and look at what a call actually costs.
IChatClient. Register a provider once, call GetResponseAsync, stream for UIs, and use structured output to get typed objects instead of text. Mind tokens, and treat every answer as untrusted.Next → Part 5: chain everything into one repeatable workflow.
Tie it together: one repeatable workflow
You now have four pieces - context, skills & agents, MCP tools, and AI features. The last skill is orchestration: wiring them into one repeatable loop so shipping a feature is a single command, not ten manual steps.
The loop every task should follow
Plan → Build → Verify → Review → you ship. Each stage is powered by something you already built:
Step 1 - Wrap the loop in a slash command
A slash command turns the whole sequence into one word. It calls your skills, runs your tests, and invokes your agent - in order, every time:
# .claude/commands/ship.md
Ship a feature end to end. Do NOT commit - I do that.
1. Give me a short plan and wait for my OK.
2. Scaffold the vertical slice with the my-endpoint skill.
3. Write integration tests (WebApplicationFactory + Testcontainers).
4. Run `dotnet build && dotnet test`. Fix failures, repeat until green.
5. Run the aspnetcore-security-auditor agent on the new code.
6. Summarize the diff + the agent's top findings, then stop.> /ship a create-invoice endpoint
# Claude plans -> scaffolds (skill) -> tests -> reviews (agent)
# -> hands you the diff. You read it and commit.Same steps, same quality bar - 9am or a Friday deploy. That's the point of a workflow: consistency you don't have to remember.
Step 2 - Keep the gates in place
- Plan mode for anything risky - it proposes, you approve, then it touches files.
- Hooks enforce your rules automatically (format on save, block bad patterns pre-commit).
- An agent is the second pair of eyes before you commit.
- You read the diff and press commit. The AI never ships on its own.
Step 3 - Let it reach further with MCP
Because your tools from Part 3 are in the loop, the workflow isn't limited to writing code. Wire the Performance Lab MCP server into a /triage command and the AI can measure a slow endpoint, name the cause from real numbers, propose the fix, and prove it - one pass, grounded in your systems:
# .claude/commands/triage.md
Triage a slow endpoint end to end:
1. Run the performance-lab MCP tool (run_load_test) on the endpoint.
2. From the numbers, name the likely cause - ThreadPool starvation? GC? N+1?
3. Propose the minimal .NET fix and show the diff. Do NOT apply yet.
4. On my OK, apply it and re-run the test to prove it's faster.That's the payoff of the whole track: context (Part 1) + a skill's checklist (Part 2) + a real tool (Part 3) + a model call (Part 4), orchestrated into one command - and you still approve every change.
- No auto-commit, no auto-deploy from a command. Always end with a human gate.
- Keep commands small and composable -
/scaffold,/verify,/ship- not one mega-command. - If you can't explain what a step did, don't ship it.
- Write a
/shipcommand that scaffolds, tests, and reviews - then stops for you. - Add one hook (format on save, or a pre-commit guard).
- Run a full feature through the loop without leaving Claude Code.
- Share your command file in the community feed.
That's Track A. Next → Track B: build AI into your apps at depth - embeddings & semantic search, RAG, and agents. Grab a runnable project to start from, or see the full AI Roadmap for .NET.
Runnable .NET projects that match the roadmap
Each is a self-contained solution with its own README - clone it, set your own keys, run it. All on .NET 10. Read the code alongside the lessons above.
All four live in one repo: github.com/StefanTheCode/AI-in-.NET
The one-screen cheat sheet
Install
# Claude Code
npm install -g @anthropic-ai/claude-code
# The .NET AI ToolKit (skills + agents)
/plugin marketplace add StefanTheCode/dotnet-ai-toolkit
/plugin install dotnet-ai-toolkit@thecodeman-ai-toolkit
/plugin marketplace update thecodeman-ai-toolkit # get new skills
# Build an MCP server in C# (Part 3)
dotnet add package ModelContextProtocol --prerelease
# Call an LLM from .NET (Part 4)
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Extensions.AI.OpenAIWhere things live
Prompts that pull their weight
> Give me a short plan first, then wait for my OK
> This EF query is slow, optimize it
> Refactor this to the Result pattern; leave idiomatic code alone
> Write integration tests with WebApplicationFactory + Testcontainers
> Review this PR like a senior .NET engineer
> Audit the security of this API
> Don't invent problems - leave correct code aloneThe words, in plain English
Frequently asked
Does this only work with Claude Code?
The lessons use Claude Code, but the ideas map across tools: CLAUDE.md maps to Cursor rules / Copilot instructions, and skills/agents/MCP are increasingly supported everywhere.
Is the AI going to replace me?
No. It replaces the developer who refuses to use it. You stay the engineer - you review everything, and you're still responsible for what ships.
Do I need to know AI/ML math for this?
No. This is applied engineering, not data science. You won't derive a transformer. You'll ship features.
How many skills should I install?
Install a set, but reach for the 5 that match your daily work. A library of 44 you never open helps no one.
My skill isn't triggering - what's wrong?
Almost always the description. Make it specific about when to fire, add synonyms, and reopen the session.
Is it safe to let the AI edit my repo?
Use plan mode for risky changes, keep your work in git, and review diffs. Add a pre-commit hook to block bad patterns. You approve; it executes.
This is Track A. Track B is where it gets rare.
The full course - Claude Code, skills, MCP in C#, then building real AI features (LLMs, RAG, agents) into your .NET apps - is inside the community, with new video clips as I record them.
Join the community → Free newsletter (20k+)Built by Stefan Đokić - TheCodeMan · Microsoft MVP