<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oleksii Nikiforov</title>
    <description>The latest articles on DEV Community by Oleksii Nikiforov (@nikiforovall).</description>
    <link>https://dev.to/nikiforovall</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F447373%2F093609cc-666f-4147-88e8-30ac74ade4c6.JPG</url>
      <title>DEV Community: Oleksii Nikiforov</title>
      <link>https://dev.to/nikiforovall</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikiforovall"/>
    <language>en</language>
    <item>
      <title>Introducing Technical Debt Master: AI-Powered Code Analysis with Local LLMs</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Sat, 09 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/introducing-technical-debt-master-ai-powered-code-analysis-with-local-llms-2758</link>
      <guid>https://dev.to/nikiforovall/introducing-technical-debt-master-ai-powered-code-analysis-with-local-llms-2758</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Introducing &lt;code&gt;tdm&lt;/code&gt;, a CLI tool that automates technical debt discovery, triage, and resolution. &lt;code&gt;tdm&lt;/code&gt; transforms technical debt management into a proactive, automated process that works seamlessly with your existing development tools through MCP integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/tech-debt-master" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/tech-debt-master&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Motivation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tdm&lt;/code&gt; workflow&lt;/li&gt;
&lt;li&gt;
Let’s Get Started

&lt;ul&gt;
&lt;li&gt;Installation&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Technical Debt Discovery&lt;/li&gt;

&lt;li&gt;Triaging Technical Debt&lt;/li&gt;

&lt;li&gt;Technical Debt Resolution&lt;/li&gt;

&lt;li&gt;References&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;As software development continues to evolve, one thing remains constant: technical debt accumulates faster than we can manage it.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Technical Debt Master&lt;/strong&gt; (hereafter &lt;code&gt;tdm&lt;/code&gt;) comes in. With the emergence of local/&lt;a href="https://opensource.org/ai/open-weights" rel="noopener noreferrer"&gt;open-weights&lt;/a&gt; LLMs like &lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-R1" rel="noopener noreferrer"&gt;DeepSeek R1&lt;/a&gt; and &lt;a href="https://huggingface.co/openai/gpt-oss-20b" rel="noopener noreferrer"&gt;GPT-OSS&lt;/a&gt; models that can run through tools like &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; or &lt;a href="https://lmstudio.ai/" rel="noopener noreferrer"&gt;LM Studio&lt;/a&gt;, we now have an unprecedented opportunity to bring AI-powered code analysis directly to our development environments.&lt;/p&gt;

&lt;p&gt;But the advantages of using local LLMs for code analysis are compelling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy and Security:&lt;/strong&gt; Your code never leaves your environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Efficiency:&lt;/strong&gt; No API costs for analysis runs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline Capability:&lt;/strong&gt; Work without internet connectivity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control:&lt;/strong&gt; Full control over model versions and behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another compelling reason to use &lt;code&gt;tdm&lt;/code&gt; in &lt;strong&gt;automation&lt;/strong&gt;. Instead of relying on developers to remember which parts of the codebase need attention or hoping that code reviews will catch accumulating problems, &lt;code&gt;tdm&lt;/code&gt; transforms this &lt;em&gt;reactive&lt;/em&gt; approach into a &lt;em&gt;proactive&lt;/em&gt;, automated system that works alongside your existing development workflow. More importantly, you catch architectural problems and code quality issues before they become expensive to fix, fundamentally shifting the cost curve in your favor.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;tdm&lt;/code&gt; workflow
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tdm&lt;/code&gt; implements a structured, phase-based approach to technical debt management:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Phase1️⃣ - &lt;strong&gt;Discovery&lt;/strong&gt;. Continuous scanning identifies new technical debt as it’s introduced and AI-powered analysis provides context and severity assessment.&lt;/li&gt;
&lt;li&gt;Phase2️⃣ - &lt;strong&gt;Triage&lt;/strong&gt;. This stage offers two workflows: interactive CLI for individual developers to review and prioritize issues directly in the terminal, or HTML reports for team-based collaborative sessions where developers can discuss priorities together.&lt;/li&gt;
&lt;li&gt;Phase3️⃣ - &lt;strong&gt;Resolution&lt;/strong&gt;. The resolution stage leverages MCP server to enable automated technical debt remediation. Once triaged, identified technical debt items are exposed through the MCP server, effectively creating a structured backlog that AI coding agents can consume and act upon. This approach provides great flexibility - you can integrate &lt;code&gt;tdm&lt;/code&gt; with your existing AI tools such as GitHub Copilot, Cursor, Claude Code CLI, Gemini CLI, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Let’s Get Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Install &lt;code&gt;tdm&lt;/code&gt; from source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet cake &lt;span class="nt"&gt;--target&lt;/span&gt; pack
dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; &lt;span class="nt"&gt;--add-source&lt;/span&gt; ./Artefacts TechDebtMaster.Cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;code&gt;tdm help&lt;/code&gt; to get a helpful reference for all available commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tdm &lt;span class="nb"&gt;help&lt;/span&gt;
&lt;span class="c"&gt;# Available Commands:&lt;/span&gt;

&lt;span class="c"&gt;# Repository Management&lt;/span&gt;
&lt;span class="c"&gt;# repo Repository management and indexing operations&lt;/span&gt;
&lt;span class="c"&gt;# ├─ index [path] Index repository content&lt;/span&gt;
&lt;span class="c"&gt;# └─ status [path] Show status of previous analysis and repository changes&lt;/span&gt;

&lt;span class="c"&gt;# Debt Analysis&lt;/span&gt;
&lt;span class="c"&gt;# debt Technical debt analysis and reporting&lt;/span&gt;
&lt;span class="c"&gt;# ├─ analyze [path] Perform debt analysis on all indexed files&lt;/span&gt;
&lt;span class="c"&gt;# ├─ show [path] Show technical debt statistics in a tree structure grouped by tags&lt;/span&gt;
&lt;span class="c"&gt;# ├─ view [path] View detailed content of specific technical debt items&lt;/span&gt;
&lt;span class="c"&gt;# ├─ report [path] Generate an interactive HTML report of technical debt&lt;/span&gt;
&lt;span class="c"&gt;# └─ import [report-file] Import modified HTML report to update analysis data&lt;/span&gt;

&lt;span class="c"&gt;# System Management&lt;/span&gt;
&lt;span class="c"&gt;# init Initialize tdm in the current repository&lt;/span&gt;
&lt;span class="c"&gt;# config Manage configuration settings&lt;/span&gt;
&lt;span class="c"&gt;# ├─ show Display current configuration&lt;/span&gt;
&lt;span class="c"&gt;# └─ set [key] [value] Set a configuration value&lt;/span&gt;
&lt;span class="c"&gt;# prompts Manage prompt templates&lt;/span&gt;
&lt;span class="c"&gt;# ├─ edit Edit a prompt template&lt;/span&gt;
&lt;span class="c"&gt;# ├─ restore Restore prompt templates to default state&lt;/span&gt;
&lt;span class="c"&gt;# └─ set-default Set the default prompt template&lt;/span&gt;
&lt;span class="c"&gt;# mcp Start Model Context Protocol server&lt;/span&gt;
&lt;span class="c"&gt;# clean Remove the .tdm folder from the current directory&lt;/span&gt;
&lt;span class="c"&gt;# help Show this detailed help information&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;You can manage &lt;code&gt;tdm&lt;/code&gt; configuration settings using the &lt;code&gt;tdm config&lt;/code&gt; command. This allows you to customize various aspects of the tool to better fit your needs.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tdm&lt;/code&gt; integrates with OpenTelemetry to export detailed telemetry data including token consumption, model inference times, and per-analysis cost breakdowns, providing precise visibility into codebase analysis costs. In this blog post I will use it in conjunction with the &lt;a href="https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/telemetry" rel="noopener noreferrer"&gt;.NET Aspire Dashboard&lt;/a&gt;. Also, we will use Aspire to work with Ollama to deploy local LLM models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOllama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ollama"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithImageTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.6.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithOpenWebUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithImageTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.5.20"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDataVolume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithLifetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ContainerLifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deepseek-r1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"deepseek-r1:1.5b"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// (alternatively) if you have free 16GB of RAM you can try something like&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gpt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gpt-oss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gpt-oss:20b"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// (optional) we use it to get a connecting string and otel configuration&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TechDebtMaster_Cli&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"tdm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"help"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can run it using &lt;a href="https://learn.microsoft.com/en-us/dotnet/aspire/cli/overview" rel="noopener noreferrer"&gt;Aspire CLI&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aspire run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Ftdm%2Faspire-dashboard.png" width="800" height="450"&gt;&lt;/center&gt;

&lt;p&gt;Now we can configure &lt;code&gt;tdm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://localhost:21050
&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_HEADERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x-otlp-api-key&lt;span class="o"&gt;=&lt;/span&gt;ddc6b5e2c7f7ef486697b3a60a9aee52
&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grpc
tdm config &lt;span class="nb"&gt;set &lt;/span&gt;ai.provider ollama
tdm config &lt;span class="nb"&gt;set &lt;/span&gt;ai.url http://localhost:62604
tdm config &lt;span class="nb"&gt;set &lt;/span&gt;ai.model &lt;span class="s1"&gt;'deepseek-r1:1.5b'&lt;/span&gt;
tdm config &lt;span class="nb"&gt;set &lt;/span&gt;default.include &lt;span class="s1"&gt;'\.cs$'&lt;/span&gt; &lt;span class="c"&gt;# we will use it with C# code base, so it makes sense to analyze only C# files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beautiful part about &lt;code&gt;tdm&lt;/code&gt; is that you can customize analysis prompts to better fit your project’s needs. Run &lt;code&gt;tdm prompts edit&lt;/code&gt; to open the prompt in your default system editor.&lt;/p&gt;

&lt;p&gt;💡 For example, you can configure &lt;code&gt;tdm&lt;/code&gt; to identify specifically security-related issues by creating dedicated &lt;code&gt;security.prompty&lt;/code&gt; file. &lt;code&gt;tdm&lt;/code&gt; leverages &lt;a href="https://www.prompty.ai/" rel="noopener noreferrer"&gt;Prompty&lt;/a&gt; for prompt management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Debt Discovery
&lt;/h2&gt;

&lt;p&gt;Initialize &lt;code&gt;tdm&lt;/code&gt; and index the repository with technical debt. In our case it is called &lt;code&gt;tdm-testproject-monkeymcp&lt;/code&gt; (some project with technical debt)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tdm init
&lt;span class="c"&gt;# √ Updated .gitignore to include .tdm folder&lt;/span&gt;
&lt;span class="c"&gt;# √ TechDebtMaster initialization complete!&lt;/span&gt;
tdm repo index
&lt;span class="c"&gt;# Analyzing repository: .&lt;/span&gt;
&lt;span class="c"&gt;# Include pattern (from default.include): \.cs$ (only files matching this pattern will be analyzed)&lt;/span&gt;

&lt;span class="c"&gt;# √ Repository analyzed successfully!&lt;/span&gt;
tdm repo status
&lt;span class="c"&gt;# Analysis Status for: .&lt;/span&gt;
&lt;span class="c"&gt;# Last analyzed: 2025-08-09 15:53:45 UTC&lt;/span&gt;

&lt;span class="c"&gt;# Recent Changes:&lt;/span&gt;
&lt;span class="c"&gt;# New files (6):&lt;/span&gt;
&lt;span class="c"&gt;# + MonkeyMCP/Program.cs&lt;/span&gt;
&lt;span class="c"&gt;# + MonkeyMCPShared/MonkeyPrompts.cs&lt;/span&gt;
&lt;span class="c"&gt;# + MonkeyMCPShared/MonkeyResources.cs&lt;/span&gt;
&lt;span class="c"&gt;# + MonkeyMCPShared/MonkeyTools.cs&lt;/span&gt;
&lt;span class="c"&gt;# + MonkeyMCPSSE/Program.cs&lt;/span&gt;
&lt;span class="c"&gt;# ... and 1 more&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are ready to perform technical debt analysis for the configured prompt using.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tdm debt analyze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Ftdm%2Ftdm-analyze.png" width="800" height="216"&gt;&lt;/center&gt;

&lt;p&gt;Here is an example of data we can find in the Aspire Dashboard, each command has a separate trace:&lt;/p&gt;

&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Ftdm%2Ftdm-traces-1.png" width="800" height="209"&gt;&lt;/center&gt;

&lt;p&gt;💡 Each command has a separate trace&lt;/p&gt;

&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Ftdm%2Ftdm-traces-2.png" width="800" height="388"&gt;&lt;/center&gt;

&lt;p&gt;💡 You can drill down to see individual traces for find per-file analysis traces.&lt;/p&gt;

&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Ftdm%2Ftdm-metrics.png" width="800" height="308"&gt;&lt;/center&gt;

&lt;p&gt;💡 Metrics give you insights into the overall LLM usage.&lt;/p&gt;

&lt;p&gt;Once we have a backlog of technical debt identified, we can prioritize and address it effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triaging Technical Debt
&lt;/h2&gt;

&lt;p&gt;Triaging technical debt means prioritizing identified issues based on business impact and remediation effort. &lt;code&gt;tdm&lt;/code&gt; enables effective team triaging through structured reports where developers can collaboratively assess technical complexity against business priorities.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;tdm debt show&lt;/code&gt; to get a tree view of technical debt items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tdm debt show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Ftdm%2Ftdm-debt-show.png" width="800" height="269"&gt;&lt;/center&gt;

&lt;p&gt;You can work with items using &lt;code&gt;tdm debt view --interactive&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tdm debt view &lt;span class="nt"&gt;--interactive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;
    
&lt;/center&gt;

&lt;p&gt;💡 In the demo above, I showcased how to interactively view, manage, and export backlog items in various formats. For example, using &lt;code&gt;--xml&lt;/code&gt; can be useful if you prefer not to use MCP and instead want to export backlog items in a more portable format. Similarly, a command like &lt;code&gt;tdm debt show --json&lt;/code&gt; would also work.&lt;/p&gt;

&lt;p&gt;If you are not a CLI-type of person, you can use &lt;code&gt;tdm debt report&lt;/code&gt; to open a web-based interface for managing your technical debt. The neat trick here is that you can actually save the report once you are done and import it back to &lt;code&gt;tdm&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tdm debt report &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;
    
&lt;/center&gt;

&lt;p&gt;💡 Use &lt;code&gt;tdm debt import report.html --apply&lt;/code&gt; to export modified report back to &lt;code&gt;tdm&lt;/code&gt;.&lt;/p&gt;

&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Ftdm%2Ftdm-debt-report.png" width="800" height="383"&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  Technical Debt Resolution
&lt;/h2&gt;

&lt;p&gt;Now, we can use MCP integration to work on technical debt resolution. &lt;code&gt;tdm&lt;/code&gt; provides out of the box integration/prompts for popular tools like &lt;strong&gt;GitHub Copilot&lt;/strong&gt; , &lt;strong&gt;Claude Code&lt;/strong&gt; , &lt;strong&gt;Gemini CLI&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tdm init &lt;span class="nt"&gt;--profile&lt;/span&gt; vscode &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;span class="c"&gt;# √ Created .vscode/mcp.json configuration&lt;/span&gt;
&lt;span class="c"&gt;# √ Created .github/prompts/tdm-work-on-debt.prompt.md&lt;/span&gt;
&lt;span class="c"&gt;# √ Updated .gitignore to include .tdm folder&lt;/span&gt;
&lt;span class="c"&gt;# √ TechDebtMaster initialization complete!&lt;/span&gt;
&lt;span class="c"&gt;# You can now start the MCP server with: tdm mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This initialization command produces the necessary configuration files for integrating with the MCP server.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tdm&lt;/code&gt; offers complete control over agent behavior through customizable prompt files (e.g.: &lt;code&gt;.github/prompts/tdm-work-on-debt.prompt.md&lt;/code&gt;). These files define how AI coding agents interact with your technical debt backlog via the MCP interface. You can tailor prompts to align with your team’s coding standards, preferences, and risk tolerance. This flexibility allows you to decide whether agents should automatically resolve straightforward issues or require human approval for more complex changes. The prompt-based configuration enables you to adjust the level of automation to suit different scenarios. For those feeling adventurous, you can even run a coding agent like Claude Code in “YOLO mode” with a simple prompt such as “resolve technical debt.”. The benefit of using &lt;code&gt;tdm&lt;/code&gt; in this case is that coding agent can focus on resolving exactly technical debt you triaged previously.&lt;/p&gt;

&lt;p&gt;For example, here is a default prompt generated during vscode profile initialization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
mode: agent
tools: ['changes', 'codebase', 'editFiles', 'fetch', 'findTestFiles', 'problems', 'runCommands', 'runTasks', 'search', 'searchResults', 'terminalLastCommand', 'terminalSelection', 'testFailure', 'usages', 'tdm-get-item', 'tdm-list-items', 'tdm-remove-item', 'tdm-show-repo-stats']
description: 'An autonomous workflow for identifying, analyzing, and resolving technical debt in a codebase to improve maintainability and efficiency.'
---
## Workflow
Execute the following workflow to systematically address technical debt:

### 1. Assessment Phase
- Use `tdm-show-repo-stats` to gather repository-wide technical debt metrics

### 2. Prioritization Phase
- Use `tdm-list-items` to retrieve first page of technical debt items

### 3. Verification Phase
- For each item in the list:
- Use `tdm-get-item` to fetch detailed information about the item
- Present the item to the user for review
- Ask user for confirmation to proceed with the item

### 4. Resolution Phase
- Use `tdm-get-item` to fetch detailed item information
- Present user with the item
- Analyze item validity:
- Review related code
- Verify if debt is still relevant
- Document investigation findings
- For each valid item:
- Implement necessary fixes
- Remove resolved items using `tdm-remove-item`

### 5. Validation Requirements
- Ensure all changes maintain existing functionality
- Document any architectural decisions
- Request human review for complex changes
- Once an item is resolved or is no longer relevant, remove it from the list using 'tdm-remove-item'.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🤖Let’s see it in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tdm mcp
&lt;span class="c"&gt;# Starting MCP server for repository: .&lt;/span&gt;
&lt;span class="c"&gt;# Server will listen on: http://localhost:3001&lt;/span&gt;
&lt;span class="c"&gt;# Press Ctrl+C to stop the server&lt;/span&gt;

&lt;span class="c"&gt;# √ MCP server started successfully&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And run &lt;code&gt;/tdm-work-on-debt&lt;/code&gt; prompt for &lt;strong&gt;GitHub Copilot&lt;/strong&gt; chat window:&lt;/p&gt;

&lt;center&gt;
    
&lt;/center&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/tech-debt-master" rel="noopener noreferrer"&gt;github.com/NikiforovAll/tech-debt-master&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have feedback, questions, or want to contribute, feel free to open an issue or pull request on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/NikiforovAll/tech-debt-master" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/tech-debt-master&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openai.com/open-models/" rel="noopener noreferrer"&gt;https://openai.com/open-models/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/openai/gpt-oss-20b" rel="noopener noreferrer"&gt;https://huggingface.co/openai/gpt-oss-20b&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Monitoring Claude Code with .NET Aspire Dashboard</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Sat, 14 Jun 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/monitoring-claude-code-with-net-aspire-dashboard-1lk3</link>
      <guid>https://dev.to/nikiforovall/monitoring-claude-code-with-net-aspire-dashboard-1lk3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.anthropic.com/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; is a powerful AI coding assistant that can help you with various development tasks. It support telemetry monitoring, so I thought it would be fun to set up a .NET Aspire dashboard to visualize my usage and see how claude operates under the hood.&lt;/p&gt;

&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fclaude-code%2Flogs-claude-code.png" width="800" height="450"&gt;&lt;/center&gt;




&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/claude-code-rules" rel="noopener noreferrer"&gt;github.com/NikiforovAll/claude-code-rules&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up .NET Aspire Dashboard
&lt;/h2&gt;

&lt;p&gt;The first step is to run the &lt;a href="https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone" rel="noopener noreferrer"&gt;Aspire dashboard&lt;/a&gt;. You can do this easily with Podman or Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;podman run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 18888:18888 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 4317:18889 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; aspire-dashboard &lt;span class="se"&gt;\&lt;/span&gt;
    mcr.microsoft.com/dotnet/aspire-dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts the Aspire dashboard with the following configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard UI accessible at port 18888&lt;/li&gt;
&lt;li&gt;OpenTelemetry collector at port 4317&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuring Claude Code Telemetry
&lt;/h2&gt;

&lt;p&gt;Next, we need to configure Claude Code to send telemetry data to our Aspire dashboard. This is done through environment variables.&lt;/p&gt;

&lt;p&gt;Create an &lt;code&gt;.env&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLAUDE_CODE_ENABLE_TELEMETRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOG_USER_PROMPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4317
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grpc

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOGS_EXPORTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;otlp
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOGS_EXPORT_INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5000

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_METRICS_PROTOCOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grpc
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_METRICS_EXPORTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;otlp
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_METRIC_EXPORT_INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10000

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;claude-code
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;service.instance.id&lt;span class="o"&gt;=&lt;/span&gt;nikiforovall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then load these environment variables in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'^#'&lt;/span&gt; .env | xargs&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://docs.anthropic.com/en/docs/claude-code/monitoring-usage" rel="noopener noreferrer"&gt;Monitoring Usage&lt;/a&gt; for more details on how to configure telemetry for Claude Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Dashboard
&lt;/h2&gt;

&lt;p&gt;With everything set up, you can now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your browser and navigate to &lt;code&gt;http://localhost:18888&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use Claude Code as you normally would&lt;/li&gt;
&lt;li&gt;Watch as telemetry data appears in the Aspire dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dashboard will show various metrics and logs that can help you understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What kind of models and tools are being used&lt;/li&gt;
&lt;li&gt;How often Claude Code is invoked&lt;/li&gt;
&lt;li&gt;What prompts are being sent&lt;/li&gt;
&lt;li&gt;Token usage patterns&lt;/li&gt;
&lt;li&gt;Any errors or issues that occur
&lt;center&gt;

&lt;/center&gt;
## Benefits of Monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setting up telemetry monitoring for Claude Code offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Usage Insights&lt;/strong&gt; : Understand how Claude Code performs in real-world scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Management&lt;/strong&gt; : Monitor token usage and adjust your subscription if necessary.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>My Claude Code Usage Best Practices and Recommendations</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Fri, 13 Jun 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/my-claude-code-usage-best-practices-and-recommendations-1mcl</link>
      <guid>https://dev.to/nikiforovall/my-claude-code-usage-best-practices-and-recommendations-1mcl</guid>
      <description>&lt;p&gt;This post shares my collection of practical recommendations and principles for using Claude Code. For more details and the full source code, check out my repository:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/claude-code-rules" rel="noopener noreferrer"&gt;github.com/NikiforovAll/claude-code-rules&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Recommendations
&lt;/h2&gt;

&lt;p&gt;Here is my list of practical recommendations for using Claude Code:&lt;/p&gt;

&lt;h3&gt;
  
  
  Planning
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ask Claude to brainstorm ideas and iterate on them. Later, these ideas can be used as grounding context for your prompts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plan mode&lt;/code&gt; vs &lt;code&gt;auto-accept mode&lt;/code&gt; vs &lt;code&gt;edit mode&lt;/code&gt;: 

&lt;ul&gt;
&lt;li&gt;Verify what is about to be performed using &lt;code&gt;plan mode&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Once verified, proceed with &lt;code&gt;auto-accept mode&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Step-by-step mode is the default mode with no auto-accept.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Workflows: 

&lt;ul&gt;
&lt;li&gt;a. Explore, plan, code, commit.&lt;/li&gt;
&lt;li&gt;b. Write tests, commit; code, iterate, commit.&lt;/li&gt;
&lt;li&gt;c. Write code, screenshot results, iterate.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Ask “think hard” to trigger deep thinking: 

&lt;ul&gt;
&lt;li&gt;“think” &amp;lt; “think hard” &amp;lt; “think harder” &amp;lt; “ultrathink”&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  AI Task-Based Development
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Write a plan to an external source (e.g., file - plan.md) and use it as a checklist.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plan.prompt.md&lt;/code&gt; - use an external file as memory for task management and planning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve created a set of commands to help with AI task-based development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;/create-prd&lt;/code&gt; to create a Product Requirements Document (PRD) based on user input.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;/generate-tasks&lt;/code&gt; to create a task list from the PRD.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;/process-task-list&lt;/code&gt; to manage and track task progress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Project structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tree &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="c"&gt;# .&lt;/span&gt;
&lt;span class="c"&gt;# ├── .claude&lt;/span&gt;
&lt;span class="c"&gt;# │   ├── commands&lt;/span&gt;
&lt;span class="c"&gt;# │   │   ├── create-prd.md&lt;/span&gt;
&lt;span class="c"&gt;# │   │   ├── generate-tasks.md&lt;/span&gt;
&lt;span class="c"&gt;# │   │   └── process-task-list.md&lt;/span&gt;
&lt;span class="c"&gt;# │   └── settings.local.json&lt;/span&gt;
&lt;span class="c"&gt;# ├── .mcp.json&lt;/span&gt;
&lt;span class="c"&gt;# └── README.md&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details, please refer to &lt;a href="https://github.com/NikiforovAll/claude-code-rules" rel="noopener noreferrer"&gt;source code&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowledge Mining / Grounding
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;code&gt;/init&lt;/code&gt; command to initialize the &lt;code&gt;CLAUDE.md&lt;/code&gt; file. There’s no required format for &lt;code&gt;CLAUDE.md&lt;/code&gt; files. I recommend keeping it concise and human-readable. You can use it to store important information about your project, such as architecture, design decisions, and other relevant details that can help Claude understand your codebase better.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CLAUDE.md&lt;/code&gt; can open other files like this: &lt;code&gt;@path/to/import&lt;/code&gt;. Be careful, as this file is attached every time you submit a prompt.&lt;/li&gt;
&lt;li&gt;There are two different types: 

&lt;ul&gt;
&lt;li&gt;Project memory &lt;code&gt;./CLAUDE.md&lt;/code&gt; - share it with your team.&lt;/li&gt;
&lt;li&gt;User memory &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt; - personal preferences.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;You can use &lt;code&gt;# &amp;lt;text&amp;gt;&lt;/code&gt; to add particular memory to the &lt;code&gt;CLAUDE.md&lt;/code&gt; file.&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Use &lt;code&gt;/memory&lt;/code&gt; to edit the memory file directly. It will open the file in your default editor.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Codebase Q&amp;amp;A: Use Claude Code to ask questions about your codebase. It can help during onboarding or when you need to understand how something works.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Use hints, reference files, provide examples, mention documentation, and provide links.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Miscellaneous
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use in “pipe” mode, as Unix philosophy utils: &lt;code&gt;claude -p ""&lt;/code&gt; or &lt;code&gt;echo '' | claude -p ""&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;🗑️ &lt;code&gt;/clear&lt;/code&gt; and &lt;code&gt;/compact &amp;lt;specific prompt for aggregation&amp;gt;&lt;/code&gt; can be very helpful.&lt;/li&gt;
&lt;li&gt;🧠 If you don’t know something about Claude Code, ask it! It’s self-aware. 

&lt;ul&gt;
&lt;li&gt;E.g., What kind of tools do you have? Can you perform a web search?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  MCP Servers
&lt;/h2&gt;

&lt;p&gt;You can use MCP servers. See &lt;a href="https://docs.anthropic.com/en/docs/claude-code/mcps" rel="noopener noreferrer"&gt;claude-code/mcps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example of how to setup MCP servers, just create a &lt;code&gt;.mcp.json&lt;/code&gt; file in your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"microsoft.docs.mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://learn.microsoft.com/api/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"context7"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@upstash/context7-mcp@latest"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🎁 Bonus: Turn Claude Code into an Interactive Tutor with Microsoft Docs &amp;amp; Context7
&lt;/h4&gt;

&lt;p&gt;You can supercharge Claude Code by integrating it with Microsoft Docs and Context7. It can be useful for learning and development tasks.&lt;/p&gt;

&lt;center&gt;
    
&lt;/center&gt;

&lt;h2&gt;
  
  
  Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;⭐ Learn best practices for Claude Code: &lt;a href="https://www.anthropic.com/engineering/claude-code-best-practices" rel="noopener noreferrer"&gt;engineering/claude-code-best-practices&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;⭐ Tutorials - &lt;a href="https://docs.anthropic.com/en/docs/claude-code/tutorials" rel="noopener noreferrer"&gt;https://docs.anthropic.com/en/docs/claude-code/tutorials&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Explore common use cases for Claude Code: &lt;a href="https://docs.anthropic.com/en/docs/claude-code/common-tasks" rel="noopener noreferrer"&gt;claude-code/common-tasks&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;CLI Usage - &lt;a href="https://docs.anthropic.com/en/docs/claude-code/cli-usage" rel="noopener noreferrer"&gt;https://docs.anthropic.com/en/docs/claude-code/cli-usage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Claude Code Memory - &lt;a href="https://docs.anthropic.com/en/docs/claude-code/memory" rel="noopener noreferrer"&gt;https://docs.anthropic.com/en/docs/claude-code/memory&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;General Prompt Engineering with Claude Models - &lt;a href="https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview" rel="noopener noreferrer"&gt;https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Interactive Tutorial for Prompt Engineering - &lt;a href="https://github.com/anthropics/prompt-eng-interactive-tutorial" rel="noopener noreferrer"&gt;https://github.com/anthropics/prompt-eng-interactive-tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you have questions or want to see more, check out the &lt;a href="https://github.com/NikiforovAll/claude-code-rules" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; or leave a comment below!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hangfire MCP Server in Standalone Mode</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Thu, 29 May 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/hangfire-mcp-server-in-standalone-mode-ile</link>
      <guid>https://dev.to/nikiforovall/hangfire-mcp-server-in-standalone-mode-ile</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Enqueue background jobs using the &lt;strong&gt;Hangfire MCP server&lt;/strong&gt; in “standalone” mode using the .NET global tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/hangfire-mcp" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/hangfire-mcp&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;
    
&lt;/center&gt;




&lt;p&gt;In my &lt;a href="https://nikiforovall.blog/dotnet/2025/05/25/hangfire-mcp.html" rel="noopener noreferrer"&gt;previous&lt;/a&gt; blog post, I showed you how to set up a Hangfire MCP server in a .NET application. We built Hangfire MCP from scratch, and as a prerequisite, you need to reference an assembly that contains Hangfire jobs directly in the Hangfire MCP project. This is a great approach if you want to extend the Hangfire MCP server’s capabilities, but it is not very convenient if you just want to use the Hangfire MCP server to enqueue background jobs.&lt;/p&gt;




&lt;p&gt;In this post, I will show you how to use the Hangfire MCP server in “standalone” mode using the .NET global tool. Here is a NuGet package that I created for this purpose: &lt;a href="https://www.nuget.org/packages/Nall.HangfireMCP" rel="noopener noreferrer"&gt;Nall.HangfireMCP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how to setup it as an MCP server in VSCode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; &lt;span class="nt"&gt;--add-source&lt;/span&gt; Nall.HangfireMCP
&lt;span class="c"&gt;# You can invoke the tool using the following command: HangfireMCP&lt;/span&gt;
&lt;span class="c"&gt;# Tool 'nall.hangfiremcp' (version '1.0.0') was successfully installed.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we need to do is configure the &lt;code&gt;mcp.json&lt;/code&gt; file to add the Hangfire MCP server and set the environment variables to specify the Hangfire jobs assembly, the job discovery expression, and the connection string for Hangfire.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hangfire-mcp-standalone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HangfireMCP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--stdio"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HANGFIRE_JOBS_ASSEMBLY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path/to/Jobs.dll"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HANGFIRE_JOBS_MATCH_EXPRESSION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[?IsInterface &amp;amp;&amp;amp; contains(Name, 'Job')]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HANGFIRE_CONNECTION_STRING"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Host=localhost;Port=5432;Username=postgres;Password=postgres;Database=hangfire"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, the jobs are dynamically loaded from the specified assembly and can be enqueued using the MCP protocol. The rules for matching job names can be specified using the &lt;code&gt;HANGFIRE_JOBS_MATCH_EXPRESSION&lt;/code&gt; environment variable. For example, the expression &lt;code&gt;[?IsInterface &amp;amp;&amp;amp; contains(Name, 'Job')]&lt;/code&gt; will match all interfaces that contain “Job” in their name. It is a &lt;a href="https://jmespath.org/tutorial.html" rel="noopener noreferrer"&gt;JMESPath&lt;/a&gt; expression, so you can define how to match job names according to your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aspire (Bonus)
&lt;/h3&gt;

&lt;p&gt;Here is how to use the Hangfire MCP server in Standalone Mode using Aspire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;postgresServer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPostgres&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"postgres-server"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDataVolume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithLifetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ContainerLifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;postgresDatabase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postgresServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hangfire"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Web&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postgresDatabase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postgresDatabase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HangfireMCP_Standalone&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"hangfire-mcp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HANGFIRE_JOBS_ASSEMBLY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"path/to/Jobs.dll"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HANGFIRE_JOBS_MATCH_EXPRESSION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"[?IsInterface &amp;amp;&amp;amp; contains(Name, 'Job')]"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postgresDatabase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postgresDatabase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMCPInspector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithSSE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Hangfire MCP server can be used in standalone mode to enqueue background jobs without the need to write custom code. This provides greater flexibility and ease of use, especially when you want to quickly set up a Hangfire MCP server for job processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nikiforovall.blog/dotnet/2025/05/25/hangfire-mcp.html" rel="noopener noreferrer"&gt;https://nikiforovall.blog/dotnet/2025/05/25/hangfire-mcp.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/NikiforovAll/hangfire-mcp" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/hangfire-mcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/modelcontextprotocol/csharp-sdk" rel="noopener noreferrer"&gt;modelcontextprotocol/csharp-sdk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Background Job Scheduling Using Hangfire MCP Server</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Sun, 25 May 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/background-job-scheduling-using-hangfire-mcp-server-3gk6</link>
      <guid>https://dev.to/nikiforovall/background-job-scheduling-using-hangfire-mcp-server-3gk6</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Enqueue background jobs using the &lt;strong&gt;Hangfire MCP server&lt;/strong&gt;. Source code: &lt;a href="https://github.com/NikiforovAll/hangfire-mcp" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/hangfire-mcp&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;
    
&lt;/center&gt;




&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;I like Hangfire for background job processing in .NET applications. It has a simple and intuitive API, a powerful dashboard, and supports various storage options. Essentially, it provides everything I need for real-world applications that require background processing. However, sometimes I need to run a job with parameters in the background, and there isn’t an easy way to do it directly from the Hangfire dashboard.&lt;/p&gt;

&lt;p&gt;🤔 So, I had an idea to schedule Hangfire jobs through an MCP server. This is exactly the use case for an MCP server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setup MCP Server
&lt;/h2&gt;

&lt;p&gt;If you want to set up your own &lt;em&gt;Hangfire MCP Server&lt;/em&gt;, you can use the &lt;a href="https://www.nuget.org/packages/Nall.ModelContextProtocol.Template" rel="noopener noreferrer"&gt;Nall.ModelContextProtocol.Template&lt;/a&gt; template that I shared with you in the &lt;a href="https://nikiforovall.github.io/dotnet/2025/04/04/mcp-template-and-aspire.html" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At a high level, you need to do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up the MCP server.&lt;/li&gt;
&lt;li&gt;Register &lt;code&gt;McpServerTool&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Configure job discovery.&lt;/li&gt;
&lt;li&gt;Connect the MCP server to Hangfire using a connection string.&lt;/li&gt;
&lt;li&gt;Configure the MCP client to consume the Hangfire MCP server. For example, in VS Code, you can configure the &lt;code&gt;mcp.json&lt;/code&gt; file to add your server.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Code Deep Dive
&lt;/h2&gt;

&lt;p&gt;Let’s see how this whole thing is composed by reviewing the &lt;code&gt;AppHost/Program.cs&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;postgresServer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPostgres&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"postgres-server"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithDataVolume&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;postgresDatabase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postgresServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hangfire"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Web&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postgresDatabase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postgresDatabase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HangfireMCP&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"hangfire-mcp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postgresDatabase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postgresDatabase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMCPInspector&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithSSE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we are adding the following components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Postgres Server&lt;/strong&gt; : This is where Hangfire will store its data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Project (aka Server)&lt;/strong&gt;: This is the main web application that will perform background job processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hangfire MCP Project&lt;/strong&gt; : This is the MCP server that will expose Hangfire jobs as MCP commands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Inspector&lt;/strong&gt; : This is the MCP inspector that will allow us to interact with the Hangfire MCP server. This is very useful for debugging and testing purposes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🚀 You can clone the repository (&lt;a href="https://github.com/NikiforovAll/hangfire-mcp" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/hangfire-mcp&lt;/a&gt;) and run the project to see how it works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aspire run &lt;span class="nt"&gt;--project&lt;/span&gt; ./samples/AppHost/AppHost.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how the Aspire Dashboard looks:&lt;/p&gt;

&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fhangfire-mcp%2Faspire-dashboard.png" width="800" height="307"&gt;&lt;/center&gt;




&lt;p&gt;From a high-level perspective, here is how it works:&lt;/p&gt;

&lt;p&gt;sequenceDiagram participant User as User participant MCPHangfire as MCP Hangfire participant IBackgroundJobClient as IBackgroundJobClient participant Database as Database participant HangfireServer as Hangfire Server User-&amp;gt;&amp;gt;MCPHangfire: Enqueue Job (via MCP Client) MCPHangfire-&amp;gt;&amp;gt;IBackgroundJobClient: Send Job Message IBackgroundJobClient-&amp;gt;&amp;gt;Database: Store Job Message HangfireServer-&amp;gt;&amp;gt;Database: Fetch Job Message HangfireServer-&amp;gt;&amp;gt;HangfireServer: Process Job&lt;/p&gt;

&lt;h3&gt;
  
  
  Hangfire MCP Server
&lt;/h3&gt;

&lt;p&gt;This project is quite straightforward. We just need to map the MCP tool to a Hangfire job and call it a day. Here is how it looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerToolType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HangfireTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHangfireDynamicScheduler&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"RunJob"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;jobName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITimeJob&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- it should point to the assembly where your Hangfire jobs are defined.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jobName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code defines a Hangfire MCP tool that allows us to enqueue jobs using the MCP protocol. The &lt;code&gt;Run&lt;/code&gt; method takes the job name, method name, and optional parameters, and enqueues the job using the &lt;code&gt;IHangfireDynamicScheduler&lt;/code&gt;. The &lt;code&gt;IHangfireDynamicScheduler&lt;/code&gt; is a custom scheduler that uses reflection to find the job method in the specified assembly and enqueue it.&lt;/p&gt;

&lt;p&gt;💡 You can find the implementation of &lt;code&gt;IHangfireDynamicScheduler&lt;/code&gt; in the source code of the project.&lt;/p&gt;

&lt;p&gt;Now, you can ask GitHub Copilot to enqueue a job using something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please run the following job: #RunJob

{
    "jobName": "HangfireJobs.ISendMessageJob",
    "methodName": "ExecuteAsync",
    "parameters": {
        "text": "Hello, MCP!"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that in this case, we need to specify the exact job name and method name, as well as the parameters. Wouldn’t it be great if we could discover the jobs by asking Copilot? Let’s see how we can do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ListJobs"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Lists all jobs"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An array of job descriptors in JSON format"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ListJobs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DiscoverJobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsInterface&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITimeJob&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- it should point to the assembly where your Hangfire jobs are defined.&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we define a &lt;code&gt;ListJobs&lt;/code&gt; method that returns a list of all jobs. The &lt;code&gt;DiscoverJobs&lt;/code&gt; method uses reflection to find all jobs in the specified assembly.&lt;/p&gt;

&lt;p&gt;The idea here is that you can define rules for job discovery that are specific to your application. In this case, we are matching interfaces that end with &lt;code&gt;Job&lt;/code&gt;. In my demo application, I have two jobs defined: &lt;code&gt;ITimeJob&lt;/code&gt; and &lt;code&gt;ISendMessageJob&lt;/code&gt;. Here is how they look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ITimeJob&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ISendMessageJob&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// You can specify parameters&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Works with complex types as well&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s take a look at the MCP server from the MCP Inspector:&lt;/p&gt;

&lt;center&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fhangfire-mcp%2Finspector.png" width="800" height="450"&gt;&lt;/center&gt;




&lt;p&gt;As you can see, we have a bunch of tools available in the MCP Inspector. We can list jobs, enqueue jobs, get job status by job ID, and requeue jobs. The MCP Inspector allows us to interact with the Hangfire MCP server, making it a nice way to test and debug the MCP server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now you can easily enqueue Hangfire jobs using MCP. You can use it to schedule jobs from any MCP client, such as VS Code, or even from your own custom application. If you want to see the end-to-end demo, you can check out the video at the beginning of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/modelcontextprotocol/csharp-sdk" rel="noopener noreferrer"&gt;modelcontextprotocol/csharp-sdk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Code Review with GitHub Copilot in Visual Studio Code</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Sat, 03 May 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/code-review-with-github-copilot-in-visual-studio-code-3a1o</link>
      <guid>https://dev.to/nikiforovall/code-review-with-github-copilot-in-visual-studio-code-3a1o</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Code Review Custom Instructions&lt;/li&gt;
&lt;li&gt;
Code Review in Agent Mode

&lt;ul&gt;
&lt;li&gt;Crafting the Review Prompt&lt;/li&gt;
&lt;li&gt;Demo&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;References&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In this blog post, I will show you how to leverage GitHub Copilot’s code review capabilities in Visual Studio Code. In addition to the built-in features, I’ll introduce my own agent-based code review workflow using a custom prompt. This approach leverages Copilot’s flexibility to tailor the review process to your team’s needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/dotnet-copilot-rules" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/dotnet-copilot-rules&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Code review&lt;/em&gt; has always been a cornerstone of high-quality software development, but its importance has only grown with the advent of LLM-based development tools like GitHub Copilot. As AI-generated code becomes more prevalent, careful reading and thorough review are essential to ensure correctness, maintainability, and security.&lt;/p&gt;

&lt;p&gt;🤖 With the latest updates, Copilot code review is seamlessly integrated into Visual Studio Code, providing instant, AI-powered feedback directly in your editor.&lt;/p&gt;

&lt;p&gt;Some of the key features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Review Selection:&lt;/strong&gt; Highlight any section of code and request an initial review. Copilot will analyze the selected code and provide feedback, including suggestions for improvements or fixes, which you can apply with a single click.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review Changes:&lt;/strong&gt; Request a deeper review of all your staged or unstaged changes directly from the Source Control tab. Copilot will review your modifications and surface comments or suggestions inline and in the Problems tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📃 Find out more about these features in the &lt;a href="https://docs.github.com/en/copilot/using-github-copilot/code-review/using-copilot-code-review?tool=vscode" rel="noopener noreferrer"&gt;GitHub Copilot documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Review Custom Instructions
&lt;/h2&gt;

&lt;p&gt;I already described how to ground Copilot Code Review with custom instructions in my previous post - &lt;a href="https://nikiforovall.github.io/productivity/2025/03/08/github-copilot-instructions-for-dotnet.html" rel="noopener noreferrer"&gt;Enforcing .NET Coding Guidelines with GitHub Copilot Custom Instructions&lt;/a&gt;. Basically, it boils down to something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github.copilot.chat.reviewSelection.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github.copilot.chat.reviewSelection.instructions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".vscode/rules/csharp/coding-guidelines.md"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Code Review in Agent Mode
&lt;/h2&gt;

&lt;p&gt;Beyond the built-in features, I’ve crafted a custom code review prompt designed for agent mode. This approach aims to replicate the functionality of the &lt;a href="https://docs.github.com/en/copilot/using-github-copilot/code-review/using-copilot-code-review?tool=webui#requesting-a-pull-request-review-from-copilot" rel="noopener noreferrer"&gt;Pull Request Review from Copilot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While the standard Copilot Pull Request Review is excellent, it has certain limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s restricted to the Web UI, requiring users to leave their IDE.&lt;/li&gt;
&lt;li&gt;It cannot be used outside the GitHub environment.&lt;/li&gt;
&lt;li&gt;It offers less capability compared to the Agent mode, which allows for the use of MCPs, model switching, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 To address these limitations, I suggest you to try out my custom code review prompt. This prompt is designed to be used in the agent mode, allowing you to leverage the full power of Copilot while keeping your workflow within Visual Studio Code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Crafting the Review Prompt
&lt;/h3&gt;

&lt;p&gt;Here is the prompt I use for code reviews:&lt;/p&gt;

&lt;p&gt;🚀 &lt;a href="https://github.com/NikiforovAll/dotnet-copilot-rules/blob/main/.vscode/prompts/code-review.prompt.md" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/dotnet-copilot-rules/blob/main/.vscode/prompts/code-review.prompt.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The prompt has two logical sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt; : This section describes the purpose of the prompt and sets the context for the review.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ---
 description: Perform a code review
 ---
 ## Code Review Expert: Detailed Analysis and Best Practices

 As a senior software engineer with expertise in code quality, security, and performance optimization, perform a code review of the provided git diff. 

 Focus on delivering actionable feedback in the following areas: (Skipped for brevity)

 Format your review using clear sections and bullet points. Include inline code references where applicable.

 Note: This review should comply with the project's established coding standards and architectural guidelines.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Constraints&lt;/strong&gt; : This section outlines the specific constraints and guidelines that the code should adhere to.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ## Constraints

 * **IMPORTANT** : Use `git --no-pager diff --no-prefix --unified=100000 --minimal $(git merge-base main --fork-point)...head` to get the diff for code review.
 * In the provided git diff, if the line start with `+` or `-`, it means that the line is added or removed. If the line starts with a space, it means that the line is unchanged. If the line starts with `@@`, it means that the line is a hunk header.

 * Use markdown for each suggestion, like
     ```


     # Code Review for ${feature_description}
     Overview of the code changes, including the purpose of the feature, any relevant context, and the files involved.

     # Suggestions
     ## ${code_review_emoji} ${Summary of the suggestion, include necessary context to understand suggestion}
     * **Priority** : ${priority: (🔥/⚠️/🟡/🟢)}
     * **File** : ${relative/path/to/file}
     * **Details** : ...
     * **Example** (if applicable): ...
     * **Suggested Change** (if applicable): (code snippet...)

     ## (other suggestions...)
     ...

     # Summary


     ```
 * Use the following emojis to indicate the priority of the suggestions:
     * 🔥 Critical
     * ⚠️ High
     * 🟡 Medium
     * 🟢 Low
 * Each suggestion should be prefixed with an emoji to indicate the type of suggestion:
     * 🔧 Change request
     * ❓ Question
     * ⛏️ Nitpick
     * ♻️ Refactor suggestion
     * 💭 Thought process or concern
     * 👍 Positive feedback
     * 📝 Explanatory note or fun fact
     * 🌱 Observation for future consideration

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, we ask Copilot to get the diff by running the &lt;code&gt;git diff&lt;/code&gt; command and perform a code review based on the output from the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git --no-pager diff --no-prefix --unified=100000 --minimal $(git merge-base main --fork-point)...head

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;Let’s say we have a simple program that counts the number of capital letters in each line of a text file. We can use the &lt;code&gt;code-review.prompt.md&lt;/code&gt; file to analyze the proposed solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"path/to/your/file.txt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;FileStream&lt;/span&gt; &lt;span class="n"&gt;fileStream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;StreamReader&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StreamReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EndOfStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLineAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;CountCapitalLetters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;]"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Counts the number of capital latters in a string, the typo in word 'latters' is intentional&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;CountCapitalLetters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;param1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;param2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;param3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;param4&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsUpper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can attach the prompt by &lt;code&gt;CTRL + ALT + /&lt;/code&gt; and select the &lt;code&gt;code-review.prompt.md&lt;/code&gt; file. Then, we can ask Copilot to follow the instructions in the prompt and perform a code review.&lt;/p&gt;

&lt;center&gt;
    
&lt;/center&gt;




&lt;p&gt;✅ And here is the result of the code review:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fgithub-copilot-instructions-for-dotnet%2Fcustom-code-review-demo.png" width="800" height="450"&gt;
&lt;/center&gt;




&lt;p&gt;I would say that the review is quite good. It is a good starting point to keep chatting with Copilot and ask for more details, or even to ask it to rewrite the code based on the suggestions.&lt;/p&gt;

&lt;p&gt;💡 The great thing about this is that you can tailor the prompt to your specific needs. You can add or remove sections, change the wording, reference the project’s coding standards, etc.&lt;/p&gt;

&lt;p&gt;💡 Treat Copilot as a coworker: start “Voice Chat” mode and engage in a conversation just as you would with a teammate. Ask questions, discuss suggestions, and iterate on solutions together to get the most out of your code review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🙌 I hope you found it helpful. If you have any questions, please feel free to reach out. If you’d like to support my work, a star on GitHub would be greatly appreciated! 🙏&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/copilot/chat/prompt-crafting" rel="noopener noreferrer"&gt;Prompt engineering for Copilot Chat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/copilot/copilot-customization#_reusable-prompt-files-experimental" rel="noopener noreferrer"&gt;GitHub Copilot Customization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.promptingguide.ai/" rel="noopener noreferrer"&gt;Prompt Engineering Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/copilot/using-github-copilot/code-review/using-copilot-code-review?tool=vscode" rel="noopener noreferrer"&gt;Using GitHub Copilot code review&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Prompt Engineering with GitHub Copilot in Visual Studio Code</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Sat, 19 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/prompt-engineering-with-github-copilot-in-visual-studio-code-3kbl</link>
      <guid>https://dev.to/nikiforovall/prompt-engineering-with-github-copilot-in-visual-studio-code-3kbl</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Learn how to leverage reusable prompts in GitHub Copilot to enhance your productivity in Visual Studio Code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/dotnet-copilot-rules" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/dotnet-copilot-rules&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Prompt engineering in the right context can be a very powerful technique when working with LLMs.&lt;/p&gt;

&lt;p&gt;In this blog post, I will not dive into the details of prompt engineering. For more information on prompt engineering, check out the &lt;a href="https://www.promptingguide.ai/techniques" rel="noopener noreferrer"&gt;Prompt Engineering Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Prompt Engineering?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the context of &lt;em&gt;Visual Studio Code&lt;/em&gt; and &lt;em&gt;GitHub Copilot&lt;/em&gt;, prompt engineering allows developers to create reusable instructions that streamline workflows, enforce coding standards, and improve collaboration. By leveraging &lt;a href="https://code.visualstudio.com/docs/copilot/copilot-customization" rel="noopener noreferrer"&gt;reusable prompts&lt;/a&gt;, you can enhance Copilot’s ability to produce meaningful suggestions tailored to your specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reusable Prompt Files in GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot introduces the concept of &lt;strong&gt;reusable prompt files&lt;/strong&gt;. These files allow you to define task-specific instructions in a structured and reusable way.&lt;/p&gt;

&lt;p&gt;You can define prompts at the workspace level, making them accessible to all team members. Or you can create them at the user level, making them available across all your projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up Reusable Prompt Files for Workspaces
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Enable Prompt Files&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open your &lt;code&gt;.vscode/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;chat.promptFiles&lt;/code&gt; setting to &lt;code&gt;true&lt;/code&gt; to enable reusable prompt files.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"chat.promptFiles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"chat.promptFilesLocations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;".github/prompts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;".vscode/prompts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can create a folder named &lt;code&gt;.github/prompts&lt;/code&gt; or &lt;code&gt;.vscode/prompts&lt;/code&gt; in your workspace. Inside this folder, you can create prompt files with the &lt;code&gt;.prompt.md&lt;/code&gt; extension.&lt;/p&gt;

&lt;p&gt;Later, you can attach these prompt files as context to your prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Here is an example of my &lt;code&gt;pros-and-cons.prompt.md&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pros and Cons Analysis&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="gh"&gt;# Definition:&lt;/span&gt;
Analyze the proposed solution, focusing on its strengths and weaknesses. Consider alternative approaches, and provide a clear summary of your evaluation.

&lt;span class="gu"&gt;## Constraints&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Ask follow-up questions if needed to clarify the solution. 
&lt;span class="p"&gt;-&lt;/span&gt; If any questions arise, wait for the user to respond before proceeding with the analysis.
&lt;span class="p"&gt;-&lt;/span&gt; Provide Pros and Cons in a bulleted list format.
&lt;span class="p"&gt;-&lt;/span&gt; Highlight best practices and common pitfalls, where relevant.
&lt;span class="p"&gt;-&lt;/span&gt; Suggest alternative solutions or improvements, if applicable.
&lt;span class="p"&gt;-&lt;/span&gt; Provide pros and cons for alternative solutions if applicable. Provide at least 3 pros and cons for each alternative solution.
&lt;span class="p"&gt;-&lt;/span&gt; Use provided emojis below to enhance readability:
&lt;span class="p"&gt;-&lt;/span&gt; Use ✅ to highlight pros in the list.
&lt;span class="p"&gt;-&lt;/span&gt; Use ❌ to indicate cons in the list.
&lt;span class="p"&gt;-&lt;/span&gt; Use ✨ to highlight best practice items in the list.
&lt;span class="p"&gt;-&lt;/span&gt; Use ☝️ to indicate common pitfalls.
&lt;span class="p"&gt;-&lt;/span&gt; Use 🔒 to highlight security (InfoSec)-related topics.
&lt;span class="p"&gt;-&lt;/span&gt; End with a single-paragraph summary of your overall assessment.

&lt;span class="gu"&gt;## Structure&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Proposed Solution**&lt;/span&gt; : &lt;span class="nt"&gt;&amp;lt;NAME&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Description**&lt;/span&gt; : &lt;span class="nt"&gt;&amp;lt;DESCRIPTION&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Pros**&lt;/span&gt; :
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Cons**&lt;/span&gt; :
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Best Practices**&lt;/span&gt; :
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Common Pitfalls**&lt;/span&gt; :
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**Alternative Solution**&lt;/span&gt; : &lt;span class="nt"&gt;&amp;lt;NAME&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Description**&lt;/span&gt; : &lt;span class="nt"&gt;&amp;lt;DESCRIPTION&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**Pros**&lt;/span&gt; :
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Cons**&lt;/span&gt; :

&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🚀 Demo
&lt;/h3&gt;

&lt;p&gt;Let’s say we want to implement Basic Authentication in some application. We can use the &lt;code&gt;pros-and-cons.prompt.md&lt;/code&gt; file to analyze the proposed solution.&lt;/p&gt;

&lt;center&gt;
    
&lt;/center&gt;

&lt;p&gt;💡 The great thing about this is that you can tailor the prompt to your specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Use Cases
&lt;/h2&gt;

&lt;p&gt;1️⃣ &lt;strong&gt;&lt;a href="https://www.promptingguide.ai/techniques/knowledge" rel="noopener noreferrer"&gt;Generated Knowledge Prompting&lt;/a&gt;&lt;/strong&gt;: Rather than including every detail in your initial prompt, you can instruct Copilot to ask clarifying questions. Your responses to these questions are then incorporated as context, enabling Copilot to generate more accurate and relevant code.&lt;/p&gt;

&lt;p&gt;For example, here is my &lt;code&gt;qa.prompt.md&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Q&amp;amp;A Session&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="gh"&gt;# Definition&lt;/span&gt;
Ask me a series of yes/no questions to better understand my needs and give a more accurate recommendation.

&lt;span class="gu"&gt;## Constraints&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Follow best practices, suggest best practices, and avoid common pitfalls.
&lt;span class="p"&gt;-&lt;/span&gt; The questions should be relevant to the topic at hand.
&lt;span class="p"&gt;-&lt;/span&gt; The questions should be clear and concise.
&lt;span class="p"&gt;-&lt;/span&gt; Ask questions in batches of 5.
&lt;span class="p"&gt;-&lt;/span&gt; Do not ask more than 5 questions.
&lt;span class="p"&gt;-&lt;/span&gt; Ask all questions in one go.
&lt;span class="p"&gt;-&lt;/span&gt; Do not proceed without my answer.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2️⃣ &lt;strong&gt;Generate Repeatable Code Constructs&lt;/strong&gt;. Assume you have a specific approach to adding CRUD operations to your application. You can create a reusable prompt file that describes the process and use it in Agent mode to generate the whole feature.&lt;/p&gt;

&lt;p&gt;3️⃣ &lt;strong&gt;Prompts as Artifacts&lt;/strong&gt;. Instead of writing in the chat window directly, you can carefully craft the prompt file with necessary requirements and use it to generate the new feature. As a result, not only do you get things done, but you also have a nice artifact for future reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🙌 I hope you found it helpful. If you have any questions, please feel free to reach out. If you’d like to support my work, a star on GitHub would be greatly appreciated! 🙏&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/copilot/chat/prompt-crafting" rel="noopener noreferrer"&gt;Prompt engineering for Copilot Chat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/copilot/copilot-customization#_reusable-prompt-files-experimental" rel="noopener noreferrer"&gt;GitHub Copilot Customization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.promptingguide.ai/" rel="noopener noreferrer"&gt;Prompt Engineering Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Composable AI with .NET Aspire: Extending DIAL Assistants with Add-Ons</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Fri, 18 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/composable-ai-with-net-aspire-extending-dial-assistants-with-add-ons-d37</link>
      <guid>https://dev.to/nikiforovall/composable-ai-with-net-aspire-extending-dial-assistants-with-add-ons-d37</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In this post we will extend &lt;em&gt;AI DIAL&lt;/em&gt; with &lt;a href="https://docs.epam-rail.com/tutorials/quick-start-with-addon" rel="noopener noreferrer"&gt;Addons&lt;/a&gt; using .NET Aspire. We will build a simple TODO Assistant that allows us to manage our TODO list using natural language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/ai-dial-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/ai-dial-dotnet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Build the Assistant from Scratch

&lt;ul&gt;
&lt;li&gt;Add DIAL Core&lt;/li&gt;
&lt;li&gt;Add Assistant with Addon&lt;/li&gt;
&lt;li&gt;Deploy OpenAI Model - GPT-4o&lt;/li&gt;
&lt;li&gt;Run Everything Together&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;References&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://nikiforovall.github.io/dotnet/ai/2025/03/30/introduction-to-dial.html" rel="noopener noreferrer"&gt;previous blog post&lt;/a&gt;, I’ve introduced you to &lt;strong&gt;&lt;a href="https://epam-rail.com/platform" rel="noopener noreferrer"&gt;DIAL&lt;/a&gt;&lt;/strong&gt;, an open-source AI orchestration platform.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI DIAL&lt;/em&gt; promises to be an extensible platform for building and deploying AI applications. In this post, we will learn how to add &lt;strong&gt;Addons&lt;/strong&gt; to &lt;em&gt;AI DIAL&lt;/em&gt;. Addons are a powerful way to extend the functionality of your AI applications by integrating with external services or APIs.&lt;/p&gt;

&lt;p&gt;The great thing about &lt;em&gt;DIAL Addons&lt;/em&gt; is that they are based on &lt;a href="https://platform.openai.com/docs/actions/introduction" rel="noopener noreferrer"&gt;OpenAI GPT Actions&lt;/a&gt; (aka Plugins). At their core, GPT Actions leverage &lt;a href="https://platform.openai.com/docs/guides/function-calling" rel="noopener noreferrer"&gt;Function Calling&lt;/a&gt; to execute API calls.&lt;/p&gt;

&lt;p&gt;In this blog, we will build a &lt;code&gt;todo-assistant&lt;/code&gt; based on the official &lt;a href="https://github.com/openai/plugins-quickstart/" rel="noopener noreferrer"&gt;TODO Assistant&lt;/a&gt; from OpenAI. This Addon will allow us to manage our TODO list using natural language commands.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;AI DIAL&lt;/em&gt;, conversational agents enable direct interactions between end-users and applications or language models via the &lt;a href="https://docs.epam-rail.com/chat-design" rel="noopener noreferrer"&gt;AI DIAL Chat&lt;/a&gt; interface or &lt;a href="https://epam-rail.com/dial_api" rel="noopener noreferrer"&gt;AI DIAL API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the Assistant from Scratch
&lt;/h2&gt;

&lt;p&gt;Let’s create empty Aspire AppHost project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new aspire-apphost &lt;span class="nt"&gt;-n&lt;/span&gt; AppHost &lt;span class="nt"&gt;-o&lt;/span&gt; AppHost 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default &lt;code&gt;Program.cs&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;var builder &lt;span class="o"&gt;=&lt;/span&gt; DistributedApplication.CreateBuilder&lt;span class="o"&gt;(&lt;/span&gt;args&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

builder.Build&lt;span class="o"&gt;()&lt;/span&gt;.Run&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add DIAL Core
&lt;/h3&gt;

&lt;p&gt;📦 Let’s install Aspire Host integration - &lt;a href="https://www.nuget.org/packages/Nall.EPAM.Dial.Aspire.Hosting" rel="noopener noreferrer"&gt;Nall.EPAM.Dial.Aspire.Hosting&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Nall.EPAM.Dial.Aspire.Hosting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can add the DIAL hosting to our application. The &lt;code&gt;Program.cs&lt;/code&gt; file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithChatUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--Adds DIAL and Chat UI&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚀 Now, we can use Aspire cli to run our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aspire run &lt;span class="nt"&gt;--project&lt;/span&gt; AppHost/AppHost.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Faspire-cli-1.png" width="800" height="310"&gt;
&lt;/center&gt;

&lt;p&gt;Here is the the Aspire Dashboard:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Fapp-host-1.png" width="800" height="176"&gt;
&lt;/center&gt;

&lt;h3&gt;
  
  
  Add Assistant with Addon
&lt;/h3&gt;

&lt;p&gt;Once we deployed the DIAL Core, we can add the Addon. The Addon is a simple HTTP API that will be called by the DIAL Core when it needs to execute an action.&lt;/p&gt;

&lt;p&gt;🐋 Here is an example of Dockerfile of “TODO List” Addon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.11-alpine AS builder

RUN apk update &amp;amp;&amp;amp; apk add git
RUN git clone https://github.com/openai/plugins-quickstart.git

FROM python:3.11-alpine
COPY --from=builder /plugins-quickstart /plugins-quickstart

WORKDIR /plugins-quickstart
RUN pip install -r requirements.txt

EXPOSE 5003
CMD ["python", "main.py"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will run this addon as container using Aspire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithChatUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;todoAddonContainer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDockerfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-addon-container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contextPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"addons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dockerfilePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"TodoDockerfile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithHttpEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5003&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we wil register the Addon with the DIAL Core:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithChatUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;todoAddonContainer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDockerfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-addon-container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contextPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"addons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dockerfilePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"TodoDockerfile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithHttpEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5003&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;todoAddon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAddon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-addon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithUpstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoAddonContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TODO List"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Addon that allows to manage user's TODO list."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAssistantsBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAssistant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-assistant"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"You are assistant that helps to manage TODO list for the user. You can add, remove and view your TODOs."&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"The assistant that manages your TODO list. It can add, remove and view your TODOs."&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TODO Assistant"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAddon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoAddon&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚀 Let’s run the AppHost and see the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aspire run &lt;span class="nt"&gt;--project&lt;/span&gt; AppHost/AppHost.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the ouput of the Aspire CLI:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Fapp-cli-2.png" width="800" height="370"&gt;
&lt;/center&gt;

&lt;p&gt;And here is the Aspire Dashboard:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Fapp-host-2.png" width="800" height="237"&gt;
&lt;/center&gt;

&lt;p&gt;If you open the Chat UI, you will see the TODO Assistant:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Fassistant-chat-demo-no-model.png" width="800" height="450"&gt;
&lt;/center&gt;

&lt;p&gt;☝️ But before we can use it, we need to deploy OpenAI model that supports Function Calling.&lt;/p&gt;

&lt;p&gt;When you open Chat UI, you will see the following message - “Not available agent selected. Please, change the agent to proceed “.&lt;/p&gt;

&lt;p&gt;Let’s fix this one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy OpenAI Model - GPT-4o
&lt;/h3&gt;

&lt;p&gt;📦 We will add &lt;a href="https://learn.microsoft.com/en-us/dotnet/aspire/azureai/azureai-openai-integration" rel="noopener noreferrer"&gt;Azure OpenAI hosting integration&lt;/a&gt; to the AppHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Aspire.Hosting.Azure.CognitiveServices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s add model deployment to the AppHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;openAIApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"azure-openai-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAzureOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"openai"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureInfrastructure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;infra&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;infra&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProvisionableResources&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CognitiveServicesAccount&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisableLocalAuth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// so we can use api key&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gpt4o&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2024-08-06"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can register this model with the DIAL Core:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithChatUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpt4o&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Wait for the model to be deployed&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dialGpt4o&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenAIModelAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial-gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deploymentName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithUpstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpt4o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;openAIApiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"gpt-4o is engineered for speed and efficiency. Its advanced ability to handle complex queries with minimal resources can translate into cost savings and performance."&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithWellKnownIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WellKnownIcon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GPT4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run Everything Together
&lt;/h3&gt;

&lt;p&gt;And that is it! We have added the OpenAI model to our DIAL Core and we are ready to see how everything works together.&lt;/p&gt;

&lt;p&gt;The final &lt;code&gt;Program.cs&lt;/code&gt; file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;openAIApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"azure-openai-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAzureOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"openai"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureInfrastructure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;infra&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;infra&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProvisionableResources&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CognitiveServicesAccount&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisableLocalAuth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// so we can use api key&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gpt4o&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2024-08-06"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithChatUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dialGpt4o&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenAIModelAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial-gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deploymentName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithUpstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpt4o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;openAIApiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"gpt-4o is engineered for speed and efficiency. Its advanced ability to handle complex queries with minimal resources can translate into cost savings and performance."&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithWellKnownIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WellKnownIcon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GPT4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;todoAddonContainer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDockerfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-addon-container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contextPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"addons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dockerfilePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"TodoDockerfile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithHttpEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5003&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;todoAddon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAddon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-addon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithUpstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoAddonContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TODO List"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Addon that allows to manage user's TODO list."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAssistantsBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAssistant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-assistant"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"You are assistant that helps to manage TODO list for the user. You can add, remove and view your TODOs."&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"The assistant that manages your TODO list. It can add, remove and view your TODOs."&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TODO Assistant"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAddon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoAddon&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚀 Let’s run the AppHost again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aspire run &lt;span class="nt"&gt;--project&lt;/span&gt; AppHost/AppHost.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the output of the Aspire CLI:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Fapp-cli-3.png" width="800" height="330"&gt;
&lt;/center&gt;

&lt;p&gt;And here is the the Aspire Dashboard:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Fapp-host-3.png" width="800" height="303"&gt;
&lt;/center&gt;

&lt;p&gt;✨ Even more interesting is Aspire Graph Dashboard:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Fapp-host-graph-3.png" width="800" height="450"&gt;
&lt;/center&gt;

&lt;p&gt;Now, if you open the Chat UI, you will see the TODO Assistant. Let’s try to add some TODOs:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faddon%2Fassistant-chat-demo.png" width="800" height="450"&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog post, we have learned how to extend &lt;em&gt;AI DIAL&lt;/em&gt; with Addons using .NET Aspire. We have built a simple TODO Assistant that allows us to manage our TODO list using natural language commands. We have also learned how to deploy OpenAI model that supports Function Calling and how to integrate it with the DIAL Core.&lt;/p&gt;

&lt;p&gt;This is just the beginning of what you can do with &lt;em&gt;AI DIAL&lt;/em&gt; and Addons. You can create more complex Addons that integrate with other services or APIs, or you can create your own custom Addons that provide unique functionality.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🙌 I hope you found it helpful. If you have any questions, please feel free to reach out. If you’d like to support my work, a star on GitHub would be greatly appreciated! 🙏&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/NikiforovAll/ai-dial-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/ai-dial-dotnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://epam-rail.com" rel="noopener noreferrer"&gt;https://epam-rail.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.epam-rail.com" rel="noopener noreferrer"&gt;https://docs.epam-rail.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/epam/ai-dial" rel="noopener noreferrer"&gt;https://github.com/epam/ai-dial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.openai.com/docs/actions/introduction" rel="noopener noreferrer"&gt;https://platform.openai.com/docs/actions/introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.epam-rail.com/tutorials/quick-start-with-addon" rel="noopener noreferrer"&gt;https://docs.epam-rail.com/tutorials/quick-start-with-addon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Learn how to use Model Context Protocol (MCP) Server Template in Hybrid Mode</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Tue, 08 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/learn-how-to-use-model-context-protocol-mcp-server-template-in-hybrid-mode-2c5c</link>
      <guid>https://dev.to/nikiforovall/learn-how-to-use-model-context-protocol-mcp-server-template-in-hybrid-mode-2c5c</guid>
      <description>&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Flight.png" width="800" height="133"&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;
Create from Template

&lt;ul&gt;
&lt;li&gt;SSE vs Stdio&lt;/li&gt;
&lt;li&gt;Review the Code&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Aspire Integration&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;References&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Use the &lt;code&gt;mcp-server-hybrid&lt;/code&gt; template to be able to easily switch between &lt;code&gt;stdio&lt;/code&gt; and &lt;code&gt;sse&lt;/code&gt; transports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/mcp-template-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/mcp-template-dotnet&lt;/a&gt;&lt;/p&gt;




&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Nall.ModelContextProtocol.Template&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://nuget.org/packages/Nall.ModelContextProtocol.Template" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fnuget%2Fv%2FNall.ModelContextProtocol.Template.svg" alt="Nuget" width="86" height="20"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Nall.ModelContextProtocol.Inspector.Aspire.Hosting&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://nuget.org/packages/Nall.ModelContextProtocol.Inspector.Aspire.Hosting" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fnuget%2Fv%2FNall.ModelContextProtocol.Inspector.Aspire.Hosting.svg" alt="Nuget" width="86" height="20"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Create from Template
&lt;/h2&gt;

&lt;p&gt;Previously, I’ve shared with you the blog post - &lt;a href="https://nikiforovall.github.io/dotnet/2025/04/04/mcp-template-and-aspire.html" rel="noopener noreferrer"&gt;Simplifying Model Context Protocol (MCP) Server Development with Aspire&lt;/a&gt;. In this post we explored two ways to run the MCP server using Aspire.&lt;/p&gt;

&lt;p&gt;🎯 In reality, depending on the context, you may want to run the MCP server in different ways. For example, you may want to run the MCP server in &lt;code&gt;sse&lt;/code&gt; mode for debugging/development purposes, but in &lt;code&gt;stdio&lt;/code&gt; mode for production.&lt;/p&gt;

&lt;p&gt;In this post, I will show you how to use a simple template to create an MCP server that can be run in both modes.&lt;/p&gt;

&lt;p&gt;📦 As in my previous post, let’s install &lt;code&gt;Nall.ModelContextProtocol.Aspire.Template&lt;/code&gt; &lt;a href="https://www.nuget.org/packages/Nall.ModelContextProtocol.Template" rel="noopener noreferrer"&gt;package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nb"&gt;install &lt;/span&gt;Nall.ModelContextProtocol.Template
&lt;span class="c"&gt;# These templates matched your input: 'mcp'&lt;/span&gt;

&lt;span class="c"&gt;# Template Name Short Name Language Tags&lt;/span&gt;
&lt;span class="c"&gt;# ----------------- ----------------- -------- -------------&lt;/span&gt;
&lt;span class="c"&gt;# MCP Server mcp-server [C#] dotnet/ai/mcp&lt;/span&gt;
&lt;span class="c"&gt;# MCP Server SSE mcp-server-sse [C#] dotnet/ai/mcp&lt;/span&gt;
&lt;span class="c"&gt;# MCP Server Hybrid mcp-server-hybrid [C#] dotnet/ai/mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;➕Create an &lt;code&gt;mcp-server-hybrid&lt;/code&gt; project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; dotnet new mcp-server-hybrid &lt;span class="nt"&gt;-o&lt;/span&gt; MyAwesomeMCPServer &lt;span class="nt"&gt;-n&lt;/span&gt; MyAwesomeMCPServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can run it in two different modes:&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;SSE&lt;/em&gt; mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet run
# info: Microsoft.Hosting.Lifetime[14]
# Now listening on: http://localhost:3001
# info: Microsoft.Hosting.Lifetime[0]
# Application started. Press Ctrl+C to shut down.
# info: Microsoft.Hosting.Lifetime[0]
# Hosting environment: Development
# info: Microsoft.Hosting.Lifetime[0]
# Content root path: ${HOME}/MyAwesomeMCPServer
# info: Microsoft.Hosting.Lifetime[0]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the &lt;em&gt;MCP Inpsector&lt;/em&gt; and configure it to listen on the default address: “&lt;a href="http://localhost:3001/sse%E2%80%9D" rel="noopener noreferrer"&gt;http://localhost:3001/sse”&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @modelcontextprotocol/inspector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;em&gt;Stdio&lt;/em&gt; mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @modelcontextprotocol/inspector dotnet run &lt;span class="nt"&gt;-v&lt;/span&gt; q &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--stdio&lt;/span&gt;
&lt;span class="c"&gt;# ⚙️ Proxy server listening on port 6277&lt;/span&gt;
&lt;span class="c"&gt;# 🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀&lt;/span&gt;
&lt;span class="c"&gt;# New SSE connection&lt;/span&gt;
&lt;span class="c"&gt;# Query parameters: {&lt;/span&gt;
&lt;span class="c"&gt;# transportType: 'stdio',&lt;/span&gt;
&lt;span class="c"&gt;# command: 'dotnet',&lt;/span&gt;
&lt;span class="c"&gt;# args: 'run -v q --stdio'&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;
&lt;span class="c"&gt;# Stdio transport: command=C:\Program Files\dotnet\dotnet.exe, args=run,-v,q,--stdio&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSE vs Stdio
&lt;/h3&gt;

&lt;p&gt;The benefit of the &lt;em&gt;SSE&lt;/em&gt; mode is that you can run the MCP server with a debugger attached and/or see the logs directly. The &lt;em&gt;Stdio&lt;/em&gt; mode is slightly more complex, as it relies on the &lt;em&gt;MCP Client&lt;/em&gt; (e.g., &lt;em&gt;MCP Inspector&lt;/em&gt;) to start the server, and it disables logging on the MCP server to maintain compatibility with the &lt;em&gt;MCP Client&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, the &lt;em&gt;Stdio&lt;/em&gt; Server’s lifetime is managed by the &lt;em&gt;MCP Client&lt;/em&gt;. This makes it much easier to consume MCP servers in this mode because you typically don’t have to worry about the server’s lifetime. It is started by the &lt;em&gt;MCP Client&lt;/em&gt; and stopped when the &lt;em&gt;MCP Client&lt;/em&gt; is stopped.&lt;/p&gt;

&lt;h3&gt;
  
  
  Review the Code
&lt;/h3&gt;

&lt;p&gt;Before you start developing your own MCPs using this template, let’s take a look at the code generated by the template. Here is a content of &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMcpServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithToolsFromAssembly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcpServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerToolType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EchoTool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Echoes the message back to the client."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"hello &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 All “magic” happens in the &lt;code&gt;McpServerExtensions.cs&lt;/code&gt; class. In the code below, we check if the &lt;code&gt;--stdio&lt;/code&gt; argument is present. If it is, we configure the server to use the &lt;em&gt;Stdio&lt;/em&gt; transport. Otherwise, we use the &lt;em&gt;SSE&lt;/em&gt; transport. You don’t need to worry about how to switch between the two modes. The template does it for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IMcpServerBuilder&lt;/span&gt; &lt;span class="nf"&gt;WithMcpServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;WebApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;isStdio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--stdio"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isStdio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebHost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseUrls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://*:0"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// random port&lt;/span&gt;

        &lt;span class="c1"&gt;// logs from stderr are shown in the inspector&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;builder&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddConsole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consoleBuilder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;consoleBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogToStandardErrorThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Trace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="n"&gt;consoleBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormatterName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mcpBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isStdio&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithStdioServerTransport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mcpBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt; &lt;span class="nf"&gt;MapMcpServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;isSse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--stdio"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isSse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Aspire Integration
&lt;/h2&gt;

&lt;p&gt;Down below I demonstrate how to run the MCP server using the &lt;em&gt;Aspire&lt;/em&gt; hosting integration in two different modes simultaneously.&lt;/p&gt;

&lt;p&gt;➕ Create AppHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new aspire-apphost &lt;span class="nt"&gt;-n&lt;/span&gt; AppHost &lt;span class="nt"&gt;-o&lt;/span&gt; AppHost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📦 Install &lt;code&gt;Nall.ModelContextProtocol.Inspector.Aspire.Hosting&lt;/code&gt; &lt;a href="https://www.nuget.org/packages/Nall.ModelContextProtocol.Inspector.Aspire.Hosting" rel="noopener noreferrer"&gt;package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add ./Apphost package Nall.ModelContextProtocol.Inspector.Aspire.Hosting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔗 Add project reference to AppHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add ./AppHost/AppHost.csproj reference ./MyAwesomeMCPServer/MyAwesomeMCPServer.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code to &lt;code&gt;Program.cs&lt;/code&gt; of the AppHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyAwesomeMCPServer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMCPInspector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mcp-sse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clientPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithSSE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMCPInspector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mcp-stdio"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithStdio&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyAwesomeMCPServer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how the Aspire Dashboard looks like:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Fmcp-hybrid-aspire-dashboard.png" width="800" height="171"&gt;
&lt;/center&gt;

&lt;p&gt;And it works like a charm!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🙌 I hope you found it helpful. If you have any questions, please feel free to reach out. If you’d like to support my work, a star on GitHub would be greatly appreciated! 🙏&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/NikiforovAll/mcp-template-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/mcp-template-dotnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/docs/concepts/transports" rel="noopener noreferrer"&gt;https://modelcontextprotocol.io/docs/concepts/transports&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Simplifying Model Context Protocol (MCP) Server Development with Aspire</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Fri, 04 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/simplifying-model-context-protocol-mcp-server-development-with-aspire-1dkn</link>
      <guid>https://dev.to/nikiforovall/simplifying-model-context-protocol-mcp-server-development-with-aspire-1dkn</guid>
      <description>&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Flight.png" width="800" height="133"&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;‘Stdio’ Mode&lt;/li&gt;
&lt;li&gt;‘SSE’ Mode&lt;/li&gt;
&lt;li&gt;Stdio vs SSE&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use &lt;code&gt;Nall.ModelContextProtocol.Inspector.Aspire.Hosting&lt;/code&gt; hosting integration to run &lt;em&gt;MCP Inspector&lt;/em&gt; and integrate it with your MCP Servers using Aspire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/mcp-template-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/mcp-template-dotnet&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This blog will be in a form of tutorial, we will build a simple &lt;code&gt;echo&lt;/code&gt; MCP server by using &lt;code&gt;Nall.ModelContextProtocol.Template&lt;/code&gt; template that I’ve shared with you in the &lt;a href="https://nikiforovall.github.io/dotnet/2025/04/02/mcp-template-getting-started.html" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;🚀 Let’s get started. I want to demonstrate two ways (aka &lt;em&gt;Transports&lt;/em&gt;) to run the MCP server: &lt;code&gt;stdio&lt;/code&gt; and &lt;code&gt;sse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But first, let’s create &lt;code&gt;Aspire&lt;/code&gt; project.&lt;/p&gt;

&lt;p&gt;➕ Create AppHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new aspire-apphost &lt;span class="nt"&gt;-n&lt;/span&gt; AppHost &lt;span class="nt"&gt;-o&lt;/span&gt; AppHost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📦 Install &lt;code&gt;Nall.ModelContextProtocol.Inspector.Aspire.Hosting&lt;/code&gt; &lt;a href="https://www.nuget.org/packages/Nall.ModelContextProtocol.Inspector.Aspire.Hosting" rel="noopener noreferrer"&gt;package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add ./Apphost package Nall.ModelContextProtocol.Inspector.Aspire.Hosting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📦 As in my previous post, let’s install &lt;code&gt;Nall.ModelContextProtocol.Aspire.Template&lt;/code&gt; &lt;a href="https://www.nuget.org/packages/Nall.ModelContextProtocol.Template" rel="noopener noreferrer"&gt;package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nb"&gt;install &lt;/span&gt;Nall.ModelContextProtocol.Template
&lt;span class="c"&gt;# These templates matched your input: 'mcp'&lt;/span&gt;

&lt;span class="c"&gt;# Template Name Short Name Language Tags&lt;/span&gt;
&lt;span class="c"&gt;# -------------- -------------- -------- -------------&lt;/span&gt;
&lt;span class="c"&gt;# MCP Server mcp-server [C#] dotnet/ai/mcp&lt;/span&gt;
&lt;span class="c"&gt;# MCP Server SSE mcp-server-sse [C#] dotnet/ai/mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ‘Stdio’ Mode
&lt;/h2&gt;

&lt;p&gt;➕Create an MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; dotnet new mcp-server &lt;span class="nt"&gt;-o&lt;/span&gt; MyAwesomeMCPServer &lt;span class="nt"&gt;-n&lt;/span&gt; MyAwesomeMCPServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔗 Add project reference to AppHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add ./AppHost/AppHost.csproj reference ./MyAwesomeMCPServer/MyAwesomeMCPServer.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;Program.cs&lt;/code&gt; of the AppHost, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMCPInspector&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;WithStdio&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyAwesomeMCPServer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It run’s &lt;a href="https://www.npmjs.com/package/@modelcontextprotocol/inspector" rel="noopener noreferrer"&gt;@modelcontextprotocol/inspector&lt;/a&gt; under the hood. It is an MCP proxy that allows you to test and debug MCP servers.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Note:&lt;/strong&gt; The &lt;em&gt;Inspector&lt;/em&gt; is responsible for starting the .NET project. So, no corresponding Aspire Resource will be available on the dashboard for the ‘Stdio’ mode.&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Fstdio-demo-dashboard.png" width="800" height="132"&gt;
&lt;/center&gt;

&lt;p&gt;Open &lt;a href="http://127.0.0.1:6274" rel="noopener noreferrer"&gt;http://127.0.0.1:6274&lt;/a&gt; in your browser and click “Connect.”. Now, you can test the server using the &lt;em&gt;Inspector&lt;/em&gt; tool.&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Fstdio-demo-inspector.png" width="800" height="431"&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  ‘SSE’ Mode
&lt;/h2&gt;

&lt;p&gt;Let’s generate a new MCP server using the &lt;code&gt;sse&lt;/code&gt; transport. You can learn more about MCP transports here - &lt;a href="https://modelcontextprotocol.io/docs/concepts/transports" rel="noopener noreferrer"&gt;https://modelcontextprotocol.io/docs/concepts/transports&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;➕ Create a new MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new mcp-server-sse &lt;span class="nt"&gt;-o&lt;/span&gt; MyAwesomeMCPServerSSE &lt;span class="nt"&gt;-n&lt;/span&gt; MyAwesomeMCPServerSSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔗 Add project reference to AppHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add ./AppHost/AppHost.csproj reference ./MyAwesomeMCPServerSSE/MyAwesomeMCPServerSSE.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;Program.cs&lt;/code&gt; of the AppHost, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyAwesomeMCPServerSSE&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// NOTE, for SSE mode it's a separate project&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMCPInspector&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithSSE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Aspire Dashboard looks slightly different for the &lt;code&gt;sse&lt;/code&gt; mode. Because, this approach creates a separate &lt;code&gt;Aspire.Hosting.ApplicationModel.ProjectResource&lt;/code&gt; with an exposed &lt;code&gt;/sse&lt;/code&gt; endpoint. This endpoint allows you to seamlessly connect to the MCP server using the &lt;em&gt;Inspector&lt;/em&gt; tool.&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Fsse-demo-dashboard.png" width="800" height="121"&gt;
&lt;/center&gt;

&lt;p&gt;Open &lt;a href="http://127.0.0.1:6274" rel="noopener noreferrer"&gt;http://127.0.0.1:6274&lt;/a&gt; in your browser and click “Connect.”. Now, you can test the server using the &lt;em&gt;Inspector&lt;/em&gt; tool.&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Fsse-demo-inspector.png" width="800" height="431"&gt;
&lt;/center&gt;

&lt;p&gt;⚠️ As for now, it is not possible to specify &lt;code&gt;sse&lt;/code&gt; endpoint in the &lt;em&gt;Inspector&lt;/em&gt; command line to automatically configure &lt;em&gt;MCP Inspector&lt;/em&gt;. I plan to extend the Aspire integration when the feature will be implemented - &lt;a href="https://github.com/modelcontextprotocol/inspector/issues/239" rel="noopener noreferrer"&gt;modelcontextprotocol/inspector/issues/239&lt;/a&gt;. For now, you have to configure the &lt;em&gt;Inspector&lt;/em&gt; manually if you divert from standard configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stdio vs SSE
&lt;/h2&gt;

&lt;p&gt;I demonstrated two approaches to running MCP servers. Personally, I favor the &lt;code&gt;sse&lt;/code&gt; mode due to its transparency and ease of troubleshooting. For instance, you can launch an MCP server with a debugger attached or view the output logs directly. In contrast, the &lt;code&gt;stdio&lt;/code&gt; mode relies on the &lt;em&gt;Inspector&lt;/em&gt; to start the server, which disables logging on the MCP server to maintain compatibility with the &lt;em&gt;Inspector&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this blog post has provided you with a clear way to start building your own MCP servers using the &lt;code&gt;Aspire&lt;/code&gt; hosting integration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🙌 I hope you found it helpful. If you have any questions, please feel free to reach out. If you’d like to support my work, a star on GitHub would be greatly appreciated! 🙏&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/NikiforovAll/mcp-template-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/mcp-template-dotnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nikiforovall.github.io/dotnet/2025/04/02/mcp-template-getting-started.html" rel="noopener noreferrer"&gt;https://nikiforovall.github.io/dotnet/2025/04/02/mcp-template-getting-started.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/docs/concepts/transports" rel="noopener noreferrer"&gt;https://modelcontextprotocol.io/docs/concepts/transports&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>aspire</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
    <item>
      <title>Simplifying Model Context Protocol (MCP) Server Distribution with .NET Global Tools</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Wed, 02 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/simplifying-model-context-protocol-mcp-server-distribution-with-net-global-tools-1i8h</link>
      <guid>https://dev.to/nikiforovall/simplifying-model-context-protocol-mcp-server-distribution-with-net-global-tools-1i8h</guid>
      <description>&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Flight.png" width="800" height="133"&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;Nall.ModelContextProtocol.Template&lt;/code&gt; template to create a Model Context Protocol (MCP) server that can be distributed as a global tool:&lt;/p&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nb"&gt;install &lt;/span&gt;Nall.ModelContextProtocol.Template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/mcp-template-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/mcp-template-dotnet&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this post, I would like to show you how you can use &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools" rel="noopener noreferrer"&gt;.NET Global Tool&lt;/a&gt; to distribute your Model Context Protocol (MCP) server. I’ve created &lt;code&gt;dotnet new&lt;/code&gt; template to support this approach and I will show you how to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nb"&gt;install &lt;/span&gt;Nall.ModelContextProtocol.Template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new list mcp
&lt;span class="c"&gt;# These templates matched your input: 'mcp'&lt;/span&gt;

&lt;span class="c"&gt;# Template Name Short Name Language Tags&lt;/span&gt;
&lt;span class="c"&gt;# ------------- ---------- -------- -------------&lt;/span&gt;
&lt;span class="c"&gt;# MCP Server mcp-server [C#] dotnet/ai/mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new mcp-server &lt;span class="nt"&gt;-o&lt;/span&gt; MyAwesomeMCPServer &lt;span class="nt"&gt;-n&lt;/span&gt; MyAwesomeMCPServer &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;span class="c"&gt;# File actions would have been taken:&lt;/span&gt;
&lt;span class="c"&gt;# Create: MyAwesomeMCPServer\.vscode\launch.json&lt;/span&gt;
&lt;span class="c"&gt;# Create: MyAwesomeMCPServer\MyAwesomeMCPServer.csproj&lt;/span&gt;
&lt;span class="c"&gt;# Create: MyAwesomeMCPServer\Program.cs&lt;/span&gt;
&lt;span class="c"&gt;# Create: MyAwesomeMCPServer\Properties\launchSettings.json&lt;/span&gt;
&lt;span class="c"&gt;# Create: MyAwesomeMCPServer\README.md&lt;/span&gt;
&lt;span class="c"&gt;# Create: MyAwesomeMCPServer\appsettings.Development.json&lt;/span&gt;
&lt;span class="c"&gt;# Create: MyAwesomeMCPServer\appsettings.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create from template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new mcp-server &lt;span class="nt"&gt;-o&lt;/span&gt; MyAwesomeMCPServer &lt;span class="nt"&gt;-n&lt;/span&gt; MyAwesomeMCPServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a content of &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Hosting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateApplicationBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithStdioServerTransport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithToolsFromAssembly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerToolType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EchoTool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Echoes the message back to the client."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"hello &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is a simple echo server that listens for incoming messages and responds with a greeting. You can add more tools by creating additional methods with the &lt;code&gt;[McpServerTool]&lt;/code&gt; attribute. The &lt;code&gt;WithToolsFromAssembly()&lt;/code&gt; method automatically registers all tools in the assembly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Locally
&lt;/h3&gt;

&lt;p&gt;⚙️ Build from the project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build &lt;span class="nt"&gt;-o&lt;/span&gt; Artefacts &lt;span class="nt"&gt;-c&lt;/span&gt; Release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚀 Run the inspector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @modelcontextprotocol/inspector &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DOTNET_ENVIRONMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Production dotnet &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;PWD&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/Artefacts/MyAwesomeMCPServer.dll"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open inspector in your browser and test the server:&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fmcp%2Finspector-demo.png" width="800" height="397"&gt;
&lt;/center&gt;

&lt;h3&gt;
  
  
  Distribute as .NET Tool
&lt;/h3&gt;

&lt;p&gt;The basic idea behind this approach is to create a .NET tool that can be installed globally on the user’s machine. This allows users to run the MCP server from anywhere without needing to specify the full path to the executable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Another benefit of this approach is that you can &lt;strong&gt;package your MCP server as a NuGet package&lt;/strong&gt; , making it easy to distribute, version, and share with others.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📦 Pack from the project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet pack &lt;span class="nt"&gt;-o&lt;/span&gt; Artefacts &lt;span class="nt"&gt;-c&lt;/span&gt; Release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the tool globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; &lt;span class="nt"&gt;--add-source&lt;/span&gt; ./Artefacts MyAwesomeMCPServer
&lt;span class="c"&gt;# You can invoke the tool using the following command: MyAwesomeMCPServer&lt;/span&gt;
&lt;span class="c"&gt;# Tool 'myawesomemcpserver' (version '1.0.0') was successfully installed.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, after you installed this tool globally, you can run it from anywhere on your system. The tool will be available as &lt;code&gt;MyAwesomeMCPServer&lt;/code&gt; (or &lt;code&gt;myawesomemcpserver&lt;/code&gt;) in your terminal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 You can also create local tool manifest and install specific MCP versions per manifest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🚀 Run the inspector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @modelcontextprotocol/inspector &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DOTNET_ENVIRONMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Production myawesomemcpserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I think MCP opens up a lot of fun possibilities for building AI applications. In post, I showed you how to you &lt;code&gt;mcp-server&lt;/code&gt; template to make your life a little easier.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🙌 I hope you found it helpful. If you have any questions, please feel free to reach out. If you’d like to support my work, a star on GitHub would be greatly appreciated! 🙏&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/NikiforovAll/mcp-template-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/mcp-template-dotnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Nall.ModelContextProtocol.Template" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/Nall.ModelContextProtocol.Template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/modelcontextprotocol/csharp-sdk" rel="noopener noreferrer"&gt;https://github.com/modelcontextprotocol/csharp-sdk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/tools/custom-templates" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/dotnet/core/tools/custom-templates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Introducing AI DIAL: The Open-Source AI Orchestration Platform</title>
      <dc:creator>Oleksii Nikiforov</dc:creator>
      <pubDate>Sun, 30 Mar 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nikiforovall/introducing-ai-dial-the-open-source-ai-orchestration-platform-57kk</link>
      <guid>https://dev.to/nikiforovall/introducing-ai-dial-the-open-source-ai-orchestration-platform-57kk</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Use &lt;a href="https://epam-rail.com/" rel="noopener noreferrer"&gt;AI DIAL&lt;/a&gt; to streamline and orchestrate Software Development Lifecycle (SDLC) for enterprise Gen AI applications. In this post, I will introduce you to the platform and its capabilities and show you how to get started with it using .NET Aspire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/NikiforovAll/ai-dial-dotnet" rel="noopener noreferrer"&gt;https://github.com/NikiforovAll/ai-dial-dotnet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Key Features&lt;/li&gt;
&lt;li&gt;
Getting Started via .NET Aspire

&lt;ul&gt;
&lt;li&gt;Hosting Integration&lt;/li&gt;
&lt;li&gt;Client Integration&lt;/li&gt;
&lt;li&gt;Demo&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;References&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;AI &lt;strong&gt;&lt;a href="https://epam-rail.com/platform" rel="noopener noreferrer"&gt;DIAL&lt;/a&gt;&lt;/strong&gt; stands for &lt;strong&gt;D&lt;/strong&gt; eterministic &lt;strong&gt;I&lt;/strong&gt; ntegrator of &lt;strong&gt;A&lt;/strong&gt; pplications and &lt;strong&gt;L&lt;/strong&gt; anguage Models. It is an enterprise-grade, open-source AI orchestration platform that simplifies the development, deployment, and management of AI-driven applications. AI DIAL acts as both a development studio and an application server, enabling seamless integration between various AI models, data pipelines, and business applications.&lt;/p&gt;

&lt;p&gt;See: &lt;a href="https://www.youtube.com/watch?v=Ud2UyXjNK4I&amp;amp;list=PLhkKkML8gp_fNs5NQdztKwIr2yWnLoQfy&amp;amp;index=1" rel="noopener noreferrer"&gt;DIAL 2.0: The Open Source AI Orchestration Platform Overview - YouTube&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Farch.png" width="800" height="523"&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;strong&gt;Models Connectivity&lt;/strong&gt; - provides out-of-the-box adapters for all major LLM providers, including all models hosted in Amazon Bedrock, Google’s Vertex AI, and Azure OpenAI Service. Additionally, you can use language models from the open-source community, alternative vendors, and fine-tuned micro models, as well as self-hosted or models listed on HuggingFace or DeepSeek. As of now, there are over 75 ready-to-use adapters, with more being added regularly. If necessary, DIAL SDK can be used to develop adapters for additional models and vendors. &lt;a href="https://docs.epam-rail.com/supported-models" rel="noopener noreferrer"&gt;[Learn More]&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;Application Server &amp;amp; Unified API&lt;/strong&gt; - provides a single Unified API, based on OpenAI API, for accessing all language models, embedding models and applications. The key design principle is to create a unification layer that allows all models and applications to be interchangeable, delivering a cohesive conversational experience and future-proof development of custom GenAI applications.&lt;a href="https://epam-rail.com/dial_api" rel="noopener noreferrer"&gt;[Learn More]&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🛠️ &lt;strong&gt;Extensibility&lt;/strong&gt; - AI DIAL can be extended beyond its standard capabilities to meet specific business requirements. You can leverage the SDK to create custom model adapters and GenAI applications, and even include new application types to build fully custom implementations. AI DIAL Chat also enables the creation of custom chat UI components. &lt;a href="https://epam-rail.com/extension-framework" rel="noopener noreferrer"&gt;[Learn More]&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Customizable Chat &amp;amp; Overlay&lt;/strong&gt; - Powerful and highly customizable chat application for end-users, with enterprise-grade access control, extendable functionality and ability to add custom GenAI applications. Overlay enables a seamless embedding of chat into any existing web application. &lt;a href="https://github.com/epam/ai-dial-chat?tab=readme-ov-file#dial-chat" rel="noopener noreferrer"&gt;[Learn More]&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;➕ &lt;strong&gt;Marketplace&lt;/strong&gt; - Marketplace gives access to all conversational agents available within the organization. Additionally, the marketplace offers collaboration tools for users and supports Role-Based Access Control (RBAC) to streamline teamwork and ensure secure access to resources. &lt;a href="https://docs.epam-rail.com/marketplace" rel="noopener noreferrer"&gt;[Learn More]&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started via .NET Aspire
&lt;/h2&gt;

&lt;p&gt;Now, you know what DIAL is. Let’s see how to get started with it using &lt;a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview" rel="noopener noreferrer"&gt;.NET Aspire&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;🎯 &lt;strong&gt;Goal&lt;/strong&gt;. Let’s say we want to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create DIAL installation with two models: &lt;code&gt;deepseek-r1&lt;/code&gt; and &lt;code&gt;phi3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use DIAL Chat UI to interact with the models&lt;/li&gt;
&lt;li&gt;Use them in our application programmatically through &lt;em&gt;DIAL Unified API&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Hosting Integration
&lt;/h3&gt;

&lt;p&gt;First, we want to add DIAL hosting integration to &lt;code&gt;AppHost&lt;/code&gt; project by adding the &lt;code&gt;EPAM.Dial.Aspire.Hosting&lt;/code&gt; NuGet package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package EPAM.Dial.Aspire.Hosting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need to add the DIAL hosting integration to the &lt;code&gt;AppHost/Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOllama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ollama"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDataVolume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithOpenWebUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ollama-deepseek-r1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"deepseek-r1:1.5b"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ollama-phi3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"phi3.5"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithChatUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;deepseek&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deepseek"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;DeepSeekR1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrimaryEndpoint&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;phi3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"phi3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Phi3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrimaryEndpoint&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Api&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deepseek&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phi3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, we are adding the DIAL hosting integration to the &lt;code&gt;AppHost&lt;/code&gt; project. We are also adding two models: &lt;code&gt;ollama-deepseek-r1&lt;/code&gt; and &lt;code&gt;ollama-phi3&lt;/code&gt;, which are hosted on the Ollama server. We are also adding a DIAL Chat UI that will be available on port 3000.&lt;/p&gt;

&lt;p&gt;💡 You are not limited to only self-hosted models. AI DIAL allows you to access models from all major LLM providers, language models from the open-source community, etc. See &lt;a href="https://docs.dialx.ai/platform/supported-models" rel="noopener noreferrer"&gt;Supported Models&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Now, we can open Aspire Dashboard and explore the component graph.&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Faspire-graph-demo.png" width="800" height="450"&gt;
&lt;/center&gt;

&lt;p&gt;Let’s open the DIAL Chat UI and navigate to the marketplace to see the available models.&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Fdial-marketplace.png" width="800" height="431"&gt;
&lt;/center&gt;

&lt;p&gt;Select the &lt;code&gt;deepseek&lt;/code&gt; model and click on the “Use Model” button. You will be redirected to the chat UI, where you can interact with the model.&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Fprompt-demo.png" width="800" height="450"&gt;
&lt;/center&gt;

&lt;p&gt;The &lt;em&gt;Chat UI&lt;/em&gt; is pretty powerful. For example, you can compare the output of two models by using &lt;em&gt;Compare Mode&lt;/em&gt;. Here you can see the output of &lt;code&gt;deepseek&lt;/code&gt; and &lt;code&gt;phi3&lt;/code&gt; models side by side.&lt;/p&gt;

&lt;center&gt;
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnikiforovall.github.io%2Fassets%2Fdial%2Fcompare-models-demo.png" width="800" height="450"&gt;
&lt;/center&gt;

&lt;h3&gt;
  
  
  Client Integration
&lt;/h3&gt;

&lt;p&gt;Now, let’s see how to use the &lt;em&gt;DIAL Unified API&lt;/em&gt; in our application. We will use the &lt;code&gt;EPAM.Dial.Aspire&lt;/code&gt; NuGet package to access the &lt;code&gt;IChatClient&lt;/code&gt; implementation to trigger chat completions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package EPAM.Dial.Aspire
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we need is to add the &lt;code&gt;DialClient&lt;/code&gt; to the &lt;code&gt;Api/Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddServiceDefaults&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDialOpenAIClient&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deepseek"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;AddChatClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- client integration&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"/chat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromQuery&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FromServices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;IChatClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"You are helpful assistant. Answer the following question: '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetResponseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapDefaultEndpoints&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, we are adding the DIAL client implementation of the &lt;code&gt;IChatClient&lt;/code&gt; from &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.AI" rel="noopener noreferrer"&gt;Microsoft.Extensions.AI&lt;/a&gt; which is a standard way of integrating with LLMs in .NET.&lt;/p&gt;

&lt;p&gt;💡 Note, it is called &lt;code&gt;AddDialOpenAIClient&lt;/code&gt; because essentially, it adds &lt;a href="https://www.nuget.org/packages/OpenAI" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; under the hood. This is possible because completion part of the &lt;em&gt;Unified API&lt;/em&gt; is compatible with the OpenAI API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;🚀Now, let’s this in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:5181/chat?query=Test'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"authorName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"contents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"$type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;think&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/think&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;It seems like you might be referring to a &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; or something related to testing, but I don’t have specific information about what you’re asking. Could you clarify your needs? Are you asking about how to perform a test, taking a test as part of an educational purpose, or something else? Let me know so I can assist you better!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"messageId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chatcmpl-161"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"responseId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chatcmpl-161"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chatThreadId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"modelId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deepseek-r1:1.5b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-03-30T13:05:43+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"finishReason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inputTokenCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outputTokenCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"totalTokenCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"additionalCounts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SystemFingerprint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fp_ollama"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;🙌 In this post, I have introduced you to &lt;a href="https://epam-rail.com/" rel="noopener noreferrer"&gt;AI DIAL&lt;/a&gt;, an open-source AI orchestration platform that simplifies the development, deployment, and management of AI-driven applications. I have also shown you how to get started with it using .NET Aspire.&lt;/p&gt;

&lt;p&gt;This is just the a scratch of the surface of what you can do with DIAL. I encourage you to explore the platform and its capabilities further. Let me know if you are interested in more posts about DIAL and its features.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://epam-rail.com" rel="noopener noreferrer"&gt;https://epam-rail.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.epam-rail.com" rel="noopener noreferrer"&gt;https://docs.epam-rail.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/epam/ai-dial" rel="noopener noreferrer"&gt;https://github.com/epam/ai-dial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>aspire</category>
      <category>ai</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
