diff --git a/.gitignore b/.gitignore
index f769e12..e9d5d7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,10 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
+# appsettings files that may contain sensitive configuration
+appsettings.Production.json
+appsettings.*.local.json
+
# Mono auto generated files
mono_crash.*
diff --git a/DotNetMcp.Tests/Server/ServerCapabilitiesTests.cs b/DotNetMcp.Tests/Server/ServerCapabilitiesTests.cs
index 1db2422..253c936 100644
--- a/DotNetMcp.Tests/Server/ServerCapabilitiesTests.cs
+++ b/DotNetMcp.Tests/Server/ServerCapabilitiesTests.cs
@@ -143,7 +143,7 @@ public async Task DotnetServerCapabilities_Supports_Cancellation_IsTrue()
}
[Fact]
- public async Task DotnetServerCapabilities_Supports_Telemetry_IsFalse()
+ public async Task DotnetServerCapabilities_Supports_Telemetry_IsTrue()
{
// Act
var result = await _tools.DotnetServerCapabilities();
@@ -153,8 +153,8 @@ public async Task DotnetServerCapabilities_Supports_Telemetry_IsFalse()
.GetProperty("telemetry")
.GetBoolean();
- // Assert - Telemetry is a future feature, should be false initially
- Assert.False(telemetry);
+ // Assert - Telemetry is enabled via SDK v0.6+ (request duration logging, OpenTelemetry semantic conventions)
+ Assert.True(telemetry);
}
[Fact]
@@ -231,7 +231,7 @@ public async Task DotnetServerCapabilities_JsonSchema_MatchesExpectedStructure()
Assert.True(capabilities.Supports.StructuredErrors);
Assert.True(capabilities.Supports.MachineReadable);
Assert.True(capabilities.Supports.Cancellation);
- Assert.False(capabilities.Supports.Telemetry);
+ Assert.True(capabilities.Supports.Telemetry);
Assert.NotNull(capabilities.SdkVersions);
Assert.NotEmpty(capabilities.SdkVersions.Installed);
Assert.Equal("net10.0", capabilities.SdkVersions.Recommended);
diff --git a/DotNetMcp/Server/ServerCapabilities.cs b/DotNetMcp/Server/ServerCapabilities.cs
index 0b60419..4a303ee 100644
--- a/DotNetMcp/Server/ServerCapabilities.cs
+++ b/DotNetMcp/Server/ServerCapabilities.cs
@@ -63,7 +63,9 @@ public sealed class ServerFeatureSupport
public bool Cancellation { get; init; }
///
- /// Whether the server supports telemetry reporting (future feature)
+ /// Whether the server supports telemetry reporting.
+ /// When enabled, the server emits request duration logs and follows OpenTelemetry semantic conventions (SDK v0.6+).
+ /// See doc/telemetry.md for configuration details.
///
[JsonPropertyName("telemetry")]
public bool Telemetry { get; init; }
diff --git a/DotNetMcp/Tools/Cli/DotNetCliTools.Misc.cs b/DotNetMcp/Tools/Cli/DotNetCliTools.Misc.cs
index ad7c89c..44743d1 100644
--- a/DotNetMcp/Tools/Cli/DotNetCliTools.Misc.cs
+++ b/DotNetMcp/Tools/Cli/DotNetCliTools.Misc.cs
@@ -69,7 +69,7 @@ public async partial Task DotnetServerCapabilities()
StructuredErrors = true,
MachineReadable = true,
Cancellation = true,
- Telemetry = false // Future feature
+ Telemetry = true // SDK v0.6.0-preview.1 provides request duration logging and OpenTelemetry semantic conventions
},
SdkVersions = new SdkVersionInfo
{
diff --git a/README.md b/README.md
index a339798..c1d1b2f 100644
--- a/README.md
+++ b/README.md
@@ -1063,6 +1063,7 @@ Key files to start with:
- 📖 [AI Assistant Best Practices Guide](doc/ai-assistant-guide.md) - **Workflows, prompts, integration patterns, and troubleshooting**
- 📖 [Machine-Readable JSON Contract](doc/machine-readable-contract.md) - **v1.0 stable contract for programmatic tool consumption**
- 📖 [Tool Surface Consolidation](doc/tool-surface-consolidation.md) - **Consolidated tool design and architecture**
+- 📖 [Telemetry and Observability](doc/telemetry.md) - **Request duration logging, OpenTelemetry integration, and performance monitoring**
- 📖 [SDK Integration Details](doc/sdk-integration.md) - Technical architecture and SDK usage
- 📖 [Advanced Topics](doc/advanced-topics.md) - Performance, logging, and security details
- 📖 [Releasing](doc/releasing.md) - How to cut a release (checklists + scripts)
diff --git a/appsettings.Development.json b/appsettings.Development.json
new file mode 100644
index 0000000..72272ba
--- /dev/null
+++ b/appsettings.Development.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://json.schemastore.org/appsettings.json",
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "ModelContextProtocol": "Debug",
+ "DotNetMcp": "Debug",
+ "Microsoft.Hosting": "Information"
+ }
+ },
+ "OpenTelemetry": {
+ "ServiceName": "dotnet-mcp",
+ "ServiceVersion": "1.0.0-dev",
+ "Traces": {
+ "Enabled": false,
+ "ConsoleExporter": true,
+ "OtlpExporter": {
+ "Enabled": false,
+ "Endpoint": "http://localhost:4317"
+ }
+ },
+ "Metrics": {
+ "Enabled": false,
+ "ConsoleExporter": true,
+ "OtlpExporter": {
+ "Enabled": false,
+ "Endpoint": "http://localhost:4317"
+ }
+ }
+ }
+}
diff --git a/appsettings.json b/appsettings.json
new file mode 100644
index 0000000..1db779c
--- /dev/null
+++ b/appsettings.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://json.schemastore.org/appsettings.json",
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "ModelContextProtocol": "Information",
+ "DotNetMcp": "Information",
+ "Microsoft.Hosting": "Warning"
+ }
+ },
+ "OpenTelemetry": {
+ "ServiceName": "dotnet-mcp",
+ "ServiceVersion": "1.0.0",
+ "Traces": {
+ "Enabled": false,
+ "ConsoleExporter": false,
+ "OtlpExporter": {
+ "Enabled": false,
+ "Endpoint": "http://localhost:4317"
+ }
+ },
+ "Metrics": {
+ "Enabled": false,
+ "ConsoleExporter": false,
+ "OtlpExporter": {
+ "Enabled": false,
+ "Endpoint": "http://localhost:4317"
+ }
+ }
+ }
+}
diff --git a/doc/performance-baseline.md b/doc/performance-baseline.md
index e903f97..f1f4996 100644
--- a/doc/performance-baseline.md
+++ b/doc/performance-baseline.md
@@ -20,6 +20,11 @@ For comprehensive performance testing and regression detection, see [Issue #151]
- More comprehensive tool coverage
- Memory profiling
+For telemetry and observability features, see [doc/telemetry.md](./telemetry.md):
+- Request duration logging (SDK v0.6+)
+- OpenTelemetry semantic conventions
+- Distributed tracing integration
+
## Test Methodology
### Configuration
@@ -130,3 +135,48 @@ If results are consistently better:
- Cache behavior significantly affects results (warmup is critical)
- Results are informational only - tests never fail CI builds
- For production performance budgets, use BenchmarkDotNet (Issue #151)
+
+## Using Telemetry for Performance Monitoring
+
+The MCP SDK v0.6+ automatically logs request duration for all tool invocations. To monitor performance in real-time:
+
+### View Request Durations
+
+All tool executions are logged with duration at `Information` level:
+
+```bash
+# Run the server and filter for request completion logs
+dotnet-mcp 2>&1 | grep "Request handler completed"
+```
+
+Example output:
+```
+info: ModelContextProtocol.Server.McpServer[LogRequestHandlerCompleted]
+ Request handler completed: tools/call (DotnetSdkVersion) in 125ms
+
+info: ModelContextProtocol.Server.McpServer[LogRequestHandlerCompleted]
+ Request handler completed: tools/call (DotnetTemplateList) in 486ms
+```
+
+### Analyze Performance Trends
+
+Enable debug logging to see detailed execution traces:
+
+```bash
+export Logging__LogLevel__Default=Debug
+dotnet-mcp 2>&1 | tee performance-log.txt
+```
+
+Then analyze the log for:
+- Average duration by tool
+- P95 latency (95th percentile)
+- Slowest operations
+- Cache effectiveness
+
+### Compare Against Baselines
+
+Compare logged durations against the baselines in this document:
+- DotnetSdkVersion: Expected ~100ms, investigate if >200ms
+- DotnetTemplateList: Expected ~500ms (first run), ~50ms (cached)
+
+For detailed telemetry configuration and OpenTelemetry integration, see [doc/telemetry.md](./telemetry.md).
diff --git a/doc/telemetry.md b/doc/telemetry.md
new file mode 100644
index 0000000..6421e35
--- /dev/null
+++ b/doc/telemetry.md
@@ -0,0 +1,349 @@
+# Telemetry and Observability
+
+This document describes the telemetry and observability features available in dotnet-mcp, leveraging the MCP C# SDK v0.6.0-preview.1.
+
+## Overview
+
+dotnet-mcp provides comprehensive telemetry and observability through:
+
+1. **Built-in SDK Telemetry** - Request duration logging and OpenTelemetry semantic conventions (SDK v0.6+)
+2. **Structured Logging** - Microsoft.Extensions.Logging with configurable log levels
+3. **OpenTelemetry Integration** - Optional instrumentation for tools, resources, and operations
+
+## Built-in SDK Telemetry (v0.6+)
+
+The MCP C# SDK v0.6.0-preview.1 automatically provides telemetry aligned with OpenTelemetry semantic conventions.
+
+### Request Duration Logging
+
+All MCP request handlers automatically log request duration:
+
+- **`LogRequestHandlerCompleted`** - Successful request completion with duration
+- **`LogRequestHandlerException`** - Failed request with duration and exception details
+
+These logs are emitted automatically by the SDK and include:
+- Request method (tool invocation, resource access, etc.)
+- Request parameters
+- Execution duration (in milliseconds)
+- Success/failure status
+- Exception details (if applicable)
+
+### Log Examples
+
+```
+info: ModelContextProtocol.Server.McpServer[LogRequestHandlerCompleted]
+ Request handler completed: tools/call (DotnetSdkVersion) in 125ms
+
+info: ModelContextProtocol.Server.McpServer[LogRequestHandlerException]
+ Request handler failed: tools/call (DotnetProjectBuild) in 3450ms
+ Exception: System.InvalidOperationException: Build failed with exit code 1
+```
+
+## Structured Logging Configuration
+
+### Default Configuration
+
+dotnet-mcp uses Microsoft.Extensions.Logging with console output to stderr:
+
+```csharp
+builder.Logging.AddConsole(options =>
+{
+ options.LogToStandardErrorThreshold = LogLevel.Trace;
+});
+```
+
+### Log Levels
+
+Configure log levels via environment variables or `appsettings.json`:
+
+```bash
+# Set minimum log level
+export Logging__LogLevel__Default=Information
+
+# Enable debug logging for MCP SDK
+export Logging__LogLevel__ModelContextProtocol=Debug
+
+# Enable trace logging for dotnet-mcp
+export Logging__LogLevel__DotNetMcp=Trace
+```
+
+Or via `appsettings.json`:
+
+```json
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "ModelContextProtocol": "Debug",
+ "DotNetMcp": "Trace"
+ }
+ }
+}
+```
+
+> **Note**: The `appsettings.json` files in this repository are for local development of dotnet-mcp and are not packaged with the `Community.Mcp.DotNet` tool. When using the installed tool (for example via `dnx`), environment variables are the recommended way to configure logging and other settings, since they work regardless of installation method. If you prefer configuration files, create an `appsettings.json` in your working directory with the desired settings.
+
+### Log Categories
+
+- **`ModelContextProtocol.*`** - SDK-level logs (request handling, transport, serialization)
+- **`DotNetMcp.*`** - Server-level logs (tool execution, resource access, errors)
+- **`Microsoft.Hosting.*`** - Hosting infrastructure logs
+
+## OpenTelemetry Integration (Optional)
+
+For production deployments, you can integrate OpenTelemetry for distributed tracing, metrics, and advanced observability.
+
+### Installation
+
+Add OpenTelemetry packages to your deployment environment (latest stable versions recommended):
+
+```bash
+dotnet add package OpenTelemetry.Extensions.Hosting
+dotnet add package OpenTelemetry.Exporter.Console
+dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
+```
+
+### Configuration Example
+
+Create an `appsettings.OpenTelemetry.json` configuration file:
+
+```json
+{
+ "OpenTelemetry": {
+ "ServiceName": "dotnet-mcp",
+ "ServiceVersion": "1.0.0",
+ "Traces": {
+ "Enabled": true,
+ "ConsoleExporter": false,
+ "OtlpExporter": {
+ "Enabled": true,
+ "Endpoint": "http://localhost:4317"
+ }
+ },
+ "Metrics": {
+ "Enabled": true,
+ "ConsoleExporter": false,
+ "OtlpExporter": {
+ "Enabled": true,
+ "Endpoint": "http://localhost:4317"
+ }
+ }
+ }
+}
+```
+
+### Integration Code
+
+Modify `Program.cs` to add OpenTelemetry:
+
+```csharp
+using OpenTelemetry;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+using OpenTelemetry.Metrics;
+
+var builder = Host.CreateApplicationBuilder(args);
+
+// Add OpenTelemetry configuration
+var openTelemetryConfig = builder.Configuration.GetSection("OpenTelemetry");
+var serviceName = openTelemetryConfig.GetValue("ServiceName") ?? "dotnet-mcp";
+var serviceVersion = openTelemetryConfig.GetValue("ServiceVersion") ?? "1.0.0";
+
+// Configure OpenTelemetry tracing
+var tracesEnabled = openTelemetryConfig.GetSection("Traces").GetValue("Enabled");
+if (tracesEnabled)
+{
+ builder.Services.AddOpenTelemetry()
+ .ConfigureResource(resource => resource
+ .AddService(serviceName, serviceVersion: serviceVersion))
+ .WithTracing(tracing =>
+ {
+ // NOTE: To capture custom spans from your application, you must first create corresponding
+ // ActivitySource instances (e.g., new ActivitySource("DotNetMcp")) in your code.
+ // The SDK does not currently implement custom ActivitySources (see Future Enhancements).
+ // When implemented, register them here:
+ //
+ // tracing
+ // .AddSource("DotNetMcp")
+ // .AddSource("ModelContextProtocol");
+
+ var tracesSection = openTelemetryConfig.GetSection("Traces");
+ var consoleExporter = tracesSection.GetValue("ConsoleExporter");
+ if (consoleExporter)
+ tracing.AddConsoleExporter();
+
+ var otlpSection = tracesSection.GetSection("OtlpExporter");
+ var otlpEnabled = otlpSection.GetValue("Enabled");
+ if (otlpEnabled)
+ {
+ var endpoint = otlpSection.GetValue("Endpoint");
+ tracing.AddOtlpExporter(options =>
+ {
+ if (!string.IsNullOrEmpty(endpoint))
+ options.Endpoint = new Uri(endpoint);
+ });
+ }
+ });
+}
+
+// Configure OpenTelemetry metrics
+var metricsEnabled = openTelemetryConfig.GetSection("Metrics").GetValue("Enabled");
+if (metricsEnabled)
+{
+ builder.Services.AddOpenTelemetry()
+ .WithMetrics(metrics =>
+ {
+ // NOTE: To collect custom metrics from your application, you must first create corresponding
+ // Meter instances (e.g., new Meter("DotNetMcp")) in your code.
+ // The SDK does not currently implement custom Meters (see Future Enhancements).
+ // When implemented, register them here:
+ //
+ // metrics
+ // .AddMeter("DotNetMcp")
+ // .AddMeter("ModelContextProtocol");
+
+ var metricsSection = openTelemetryConfig.GetSection("Metrics");
+ var consoleExporter = metricsSection.GetValue("ConsoleExporter");
+ if (consoleExporter)
+ metrics.AddConsoleExporter();
+
+ var otlpSection = metricsSection.GetSection("OtlpExporter");
+ var otlpEnabled = otlpSection.GetValue("Enabled");
+ if (otlpEnabled)
+ {
+ var endpoint = otlpSection.GetValue("Endpoint");
+ metrics.AddOtlpExporter(options =>
+ {
+ if (!string.IsNullOrEmpty(endpoint))
+ options.Endpoint = new Uri(endpoint);
+ });
+ }
+ });
+}
+
+// Continue with standard MCP server configuration...
+builder.Services.AddMcpServer(options => { /* ... */ });
+```
+
+## Performance Metrics
+
+### Tool Execution Times
+
+The SDK automatically tracks and logs execution duration for all tool calls:
+
+- **Fast tools** (< 100ms): `DotnetSdkVersion`, `DotnetHelp`
+- **Medium tools** (100-500ms): `DotnetTemplateList`, `DotnetPackageSearch`
+- **Slow tools** (> 500ms): `DotnetProjectBuild`, `DotnetProjectTest`, `DotnetProjectPublish`
+
+See [doc/performance-baseline.md](./performance-baseline.md) for baseline performance measurements.
+
+### Resource Access Patterns
+
+Resource access is logged with duration:
+
+```
+info: ModelContextProtocol.Server.McpServer[LogRequestHandlerCompleted]
+ Request handler completed: resources/read (dotnet://info) in 45ms
+```
+
+> **Note**: The SDK logs request duration automatically. Cache status indicators like "(cached)" would require custom logging in your resource implementation.
+
+### Error Rates
+
+Failed requests are automatically logged with:
+- Error type and message
+- Request duration
+- Tool/resource name
+- Stack trace (in debug mode)
+
+## Monitoring Best Practices
+
+### 1. Enable Appropriate Log Levels
+
+For **development**:
+```bash
+export Logging__LogLevel__Default=Debug
+```
+
+For **production**:
+```bash
+export Logging__LogLevel__Default=Information
+export Logging__LogLevel__ModelContextProtocol=Warning
+```
+
+### 2. Monitor Key Metrics
+
+Track these key performance indicators:
+
+- **P95 latency** - 95th percentile request duration
+- **Error rate** - Failed requests / total requests
+- **Slow operations** - Requests > 1000ms
+- **Cache hit rate** - Cached responses / total responses
+
+### 3. Set Up Alerts
+
+Configure alerts for:
+- Error rate > 5%
+- P95 latency > 2x baseline
+- Any request > 10 seconds
+
+### 4. Use Distributed Tracing
+
+For multi-service deployments, use OpenTelemetry OTLP exporters to send traces to:
+- **Jaeger** - Open-source distributed tracing
+- **Zipkin** - Distributed tracing system
+- **Azure Monitor** - Cloud-native observability
+- **Grafana Cloud** - Managed observability platform
+
+## Telemetry Data Privacy
+
+dotnet-mcp telemetry respects user privacy:
+
+- **No sensitive data** - Command output containing secrets or credentials is never logged
+- **Sanitized parameters** - Sensitive parameters are redacted in logs
+- **Opt-in only** - OpenTelemetry integration requires explicit configuration
+- **Local by default** - Logs are written to stderr, not sent externally
+
+## Troubleshooting
+
+### Enable Debug Logging
+
+```bash
+# Enable all debug logs
+export Logging__LogLevel__Default=Debug
+
+# Run the server
+dotnet-mcp
+```
+
+### View Request Durations
+
+All request durations are logged automatically at `Information` level:
+
+```bash
+# Filter for request completion logs
+dotnet-mcp 2>&1 | grep "Request handler completed"
+```
+
+### Analyze Performance Issues
+
+1. Enable trace logging: `export Logging__LogLevel__DotNetMcp=Trace`
+2. Look for slow operations in logs (duration > 1000ms)
+3. Check for repeated slow operations (cache misses)
+4. Compare against baseline metrics in `doc/performance-baseline.md`
+
+## References
+
+- [MCP C# SDK v0.6 Release Notes](https://github.com/modelcontextprotocol/csharp-sdk/releases/tag/v0.6.0-preview.1)
+- [OpenTelemetry .NET Documentation](https://opentelemetry.io/docs/languages/net/)
+- [Performance Baseline Measurements](./performance-baseline.md)
+
+## Future Enhancements
+
+Planned telemetry improvements:
+
+- Custom ActivitySource for tool execution spans
+- Metrics for cache hit/miss rates
+- Performance budgets with automated regression detection
+- Integration with Application Insights
+- Grafana dashboard templates