Skip to content

Commit d9e06dd

Browse files
authored
Merge pull request #29 from jongalloway/logging-improvements
refactor: Add logging to TemplateEngineHelper and DotNetCliTools
2 parents 708f111 + 0767840 commit d9e06dd

File tree

2 files changed

+43
-14
lines changed

2 files changed

+43
-14
lines changed

DotNetMcp/DotNetCliTools.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.ComponentModel;
22
using System.Diagnostics;
33
using System.Text;
4+
using Microsoft.Extensions.Logging;
45
using ModelContextProtocol.Protocol;
56
using ModelContextProtocol.Server;
67

@@ -9,24 +10,31 @@ namespace DotNetMcp;
910
[McpServerToolType]
1011
public sealed class DotNetCliTools
1112
{
13+
private readonly ILogger<DotNetCliTools> _logger;
14+
15+
public DotNetCliTools(ILogger<DotNetCliTools> logger)
16+
{
17+
// DI guarantees logger is never null
18+
_logger = logger!;
19+
}
1220
[McpServerTool, Description("List all installed .NET templates with their metadata using the Template Engine. Provides structured information about available project templates.")]
1321
public async Task<string> DotnetTemplateList()
14-
=> await TemplateEngineHelper.GetInstalledTemplatesAsync();
22+
=> await TemplateEngineHelper.GetInstalledTemplatesAsync(_logger);
1523

1624
[McpServerTool, Description("Search for .NET templates by name or description. Returns matching templates with their details.")]
1725
public async Task<string> DotnetTemplateSearch(
1826
[Description("Search term to find templates (searches in name, short name, and description)")] string searchTerm)
19-
=> await TemplateEngineHelper.SearchTemplatesAsync(searchTerm);
27+
=> await TemplateEngineHelper.SearchTemplatesAsync(searchTerm, _logger);
2028

2129
[McpServerTool, Description("Get detailed information about a specific template including available parameters and options.")]
2230
public async Task<string> DotnetTemplateInfo(
2331
[Description("The template short name (e.g., 'console', 'webapi', 'classlib')")] string templateShortName)
24-
=> await TemplateEngineHelper.GetTemplateDetailsAsync(templateShortName);
32+
=> await TemplateEngineHelper.GetTemplateDetailsAsync(templateShortName, _logger);
2533

2634
[McpServerTool, Description("Clear the template cache to force reload from disk. Use this after installing or uninstalling templates.")]
2735
public async Task<string> DotnetTemplateClearCache()
2836
{
29-
await TemplateEngineHelper.ClearCacheAsync();
37+
await TemplateEngineHelper.ClearCacheAsync(_logger);
3038
return "Template cache cleared successfully. Next template query will reload from disk.";
3139
}
3240

@@ -445,6 +453,8 @@ public async Task<string> DotnetNugetLocals(
445453

446454
private async Task<string> ExecuteDotNetCommand(string arguments)
447455
{
456+
_logger.LogDebug("Executing: dotnet {Arguments}", arguments);
457+
448458
var psi = new ProcessStartInfo
449459
{
450460
FileName = "dotnet",
@@ -502,6 +512,16 @@ private async Task<string> ExecuteDotNetCommand(string arguments)
502512
process.BeginErrorReadLine();
503513
await process.WaitForExitAsync();
504514

515+
_logger.LogDebug("Command completed with exit code: {ExitCode}", process.ExitCode);
516+
if (outputTruncated)
517+
{
518+
_logger.LogWarning("Output was truncated due to size limit");
519+
}
520+
if (errorTruncated)
521+
{
522+
_logger.LogWarning("Error output was truncated due to size limit");
523+
}
524+
505525
var result = new StringBuilder();
506526
if (output.Length > 0) result.AppendLine(output.ToString());
507527
if (error.Length > 0)

DotNetMcp/TemplateEngineHelper.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Text;
22
using System.Text.Json;
3+
using Microsoft.Extensions.Logging;
34
using Microsoft.TemplateEngine.Abstractions;
45
using Microsoft.TemplateEngine.Edge;
56
using Microsoft.TemplateEngine.Edge.Settings;
@@ -32,20 +33,27 @@ public class TemplateEngineHelper
3233
/// Get templates from cache or load them if cache is expired.
3334
/// Cache expires after 5 minutes to allow for template installations/updates.
3435
/// </summary>
35-
private static async Task<IEnumerable<ITemplateInfo>> GetTemplatesCachedAsync()
36+
private static async Task<IEnumerable<ITemplateInfo>> GetTemplatesCachedAsync(ILogger? logger = null)
3637
{
3738
await _cacheLock.WaitAsync();
3839
try
3940
{
4041
if (_templatesCache == null || DateTime.UtcNow > _cacheExpiry)
4142
{
43+
logger?.LogDebug("Template cache miss - loading templates from template engine");
4244
var engineEnvironmentSettings = new EngineEnvironmentSettings(
4345
new DefaultTemplateEngineHost("dotnet-mcp", "1.0.0"),
4446
virtualizeSettings: true);
4547

4648
var templatePackageManager = new TemplatePackageManager(engineEnvironmentSettings);
4749
_templatesCache = await templatePackageManager.GetTemplatesAsync(default);
4850
_cacheExpiry = DateTime.UtcNow.Add(CacheDuration);
51+
logger?.LogInformation("Loaded {TemplateCount} templates into cache (expires in {CacheDuration})",
52+
_templatesCache.Count(), CacheDuration);
53+
}
54+
else
55+
{
56+
logger?.LogDebug("Template cache hit - returning cached templates");
4957
}
5058
return _templatesCache;
5159
}
@@ -62,13 +70,14 @@ private static async Task<IEnumerable<ITemplateInfo>> GetTemplatesCachedAsync()
6270
/// This method properly uses async/await to prevent potential deadlocks that could occur
6371
/// with synchronous Wait() calls on SemaphoreSlim.
6472
/// </remarks>
65-
public static async Task ClearCacheAsync()
73+
public static async Task ClearCacheAsync(ILogger? logger = null)
6674
{
6775
await _cacheLock.WaitAsync();
6876
try
6977
{
7078
_templatesCache = null;
7179
_cacheExpiry = DateTime.MinValue;
80+
logger?.LogInformation("Template cache cleared");
7281
}
7382
finally
7483
{
@@ -93,12 +102,12 @@ public static void Dispose()
93102
/// <summary>
94103
/// Get a list of all installed templates with their metadata.
95104
/// </summary>
96-
public static async Task<string> GetInstalledTemplatesAsync()
105+
public static async Task<string> GetInstalledTemplatesAsync(ILogger? logger = null)
97106
{
98107
try
99108
{
100109
// Get all installed templates from cache
101-
var templates = await GetTemplatesCachedAsync();
110+
var templates = await GetTemplatesCachedAsync(logger);
102111

103112
if (!templates.Any())
104113
{
@@ -139,11 +148,11 @@ public static async Task<string> GetInstalledTemplatesAsync()
139148
/// <summary>
140149
/// Get detailed information about a specific template.
141150
/// </summary>
142-
public static async Task<string> GetTemplateDetailsAsync(string templateShortName)
151+
public static async Task<string> GetTemplateDetailsAsync(string templateShortName, ILogger? logger = null)
143152
{
144153
try
145154
{
146-
var templates = await GetTemplatesCachedAsync();
155+
var templates = await GetTemplatesCachedAsync(logger);
147156
var template = templates.FirstOrDefault(t =>
148157
t.ShortNameList.Any(sn => sn.Equals(templateShortName, StringComparison.OrdinalIgnoreCase)));
149158

@@ -188,11 +197,11 @@ public static async Task<string> GetTemplateDetailsAsync(string templateShortNam
188197
/// <summary>
189198
/// Search for templates by name or description.
190199
/// </summary>
191-
public static async Task<string> SearchTemplatesAsync(string searchTerm)
200+
public static async Task<string> SearchTemplatesAsync(string searchTerm, ILogger? logger = null)
192201
{
193202
try
194203
{
195-
var templates = await GetTemplatesCachedAsync();
204+
var templates = await GetTemplatesCachedAsync(logger);
196205
var matches = templates.Where(t =>
197206
t.ShortNameList.Any(sn => sn.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)) ||
198207
(t.Name?.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) ?? false) ||
@@ -236,11 +245,11 @@ public static async Task<string> SearchTemplatesAsync(string searchTerm)
236245
/// <summary>
237246
/// Validate if a template short name exists.
238247
/// </summary>
239-
public static async Task<bool> ValidateTemplateExistsAsync(string templateShortName)
248+
public static async Task<bool> ValidateTemplateExistsAsync(string templateShortName, ILogger? logger = null)
240249
{
241250
try
242251
{
243-
var templates = await GetTemplatesCachedAsync();
252+
var templates = await GetTemplatesCachedAsync(logger);
244253
return templates.Any(t =>
245254
t.ShortNameList.Any(sn => sn.Equals(templateShortName, StringComparison.OrdinalIgnoreCase)));
246255
}

0 commit comments

Comments
 (0)