Skip to content

Commit 1b52dda

Browse files
Copilotstephentoubhalter73
authored
Add DebuggerDisplay attributes to Protocol types (#1068)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> Co-authored-by: Stephen Halter <halter73@gmail.com>
1 parent 9702079 commit 1b52dda

File tree

10 files changed

+175
-0
lines changed

10 files changed

+175
-0
lines changed

src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Text.Json.Serialization;
23

34
namespace ModelContextProtocol.Protocol;
@@ -20,11 +21,23 @@ namespace ModelContextProtocol.Protocol;
2021
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for more details.
2122
/// </para>
2223
/// </remarks>
24+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
2325
public sealed class BlobResourceContents : ResourceContents
2426
{
2527
/// <summary>
2628
/// Gets or sets the base64-encoded string representing the binary data of the item.
2729
/// </summary>
2830
[JsonPropertyName("blob")]
2931
public required string Blob { get; set; }
32+
33+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
34+
private string DebuggerDisplay
35+
{
36+
get
37+
{
38+
string lengthDisplay = DebuggerDisplayHelper.GetBase64LengthDisplay(Blob);
39+
string mimeInfo = MimeType is not null ? $", MimeType = {MimeType}" : "";
40+
return $"Uri = \"{Uri}\"{mimeInfo}, Length = {lengthDisplay}";
41+
}
42+
}
3043
}

src/ModelContextProtocol.Core/Protocol/ContentBlock.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ public override void Write(Utf8JsonWriter writer, ContentBlock value, JsonSerial
356356
}
357357

358358
/// <summary>Represents text provided to or from an LLM.</summary>
359+
[DebuggerDisplay("Text = \"{Text}\"")]
359360
public sealed class TextContentBlock : ContentBlock
360361
{
361362
/// <inheritdoc/>
@@ -366,9 +367,13 @@ public sealed class TextContentBlock : ContentBlock
366367
/// </summary>
367368
[JsonPropertyName("text")]
368369
public required string Text { get; set; }
370+
371+
/// <inheritdoc/>
372+
public override string ToString() => Text ?? "";
369373
}
370374

371375
/// <summary>Represents an image provided to or from an LLM.</summary>
376+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
372377
public sealed class ImageContentBlock : ContentBlock
373378
{
374379
/// <inheritdoc/>
@@ -388,9 +393,13 @@ public sealed class ImageContentBlock : ContentBlock
388393
/// </remarks>
389394
[JsonPropertyName("mimeType")]
390395
public required string MimeType { get; set; }
396+
397+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
398+
private string DebuggerDisplay => $"MimeType = {MimeType}, Length = {DebuggerDisplayHelper.GetBase64LengthDisplay(Data)}";
391399
}
392400

393401
/// <summary>Represents audio provided to or from an LLM.</summary>
402+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
394403
public sealed class AudioContentBlock : ContentBlock
395404
{
396405
/// <inheritdoc/>
@@ -410,12 +419,16 @@ public sealed class AudioContentBlock : ContentBlock
410419
/// </remarks>
411420
[JsonPropertyName("mimeType")]
412421
public required string MimeType { get; set; }
422+
423+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
424+
private string DebuggerDisplay => $"MimeType = {MimeType}, Length = {DebuggerDisplayHelper.GetBase64LengthDisplay(Data)}";
413425
}
414426

415427
/// <summary>Represents the contents of a resource, embedded into a prompt or tool call result.</summary>
416428
/// <remarks>
417429
/// It is up to the client how best to render embedded resources for the benefit of the LLM and/or the user.
418430
/// </remarks>
431+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
419432
public sealed class EmbeddedResourceBlock : ContentBlock
420433
{
421434
/// <inheritdoc/>
@@ -433,12 +446,16 @@ public sealed class EmbeddedResourceBlock : ContentBlock
433446
/// </remarks>
434447
[JsonPropertyName("resource")]
435448
public required ResourceContents Resource { get; set; }
449+
450+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
451+
private string DebuggerDisplay => $"Uri = \"{Resource.Uri}\"";
436452
}
437453

438454
/// <summary>Represents a resource that the server is capable of reading, included in a prompt or tool call result.</summary>
439455
/// <remarks>
440456
/// Resource links returned by tools are not guaranteed to appear in the results of `resources/list` requests.
441457
/// </remarks>
458+
[DebuggerDisplay("Name = {Name}, Uri = \"{Uri}\"")]
442459
public sealed class ResourceLinkBlock : ContentBlock
443460
{
444461
/// <inheritdoc/>
@@ -503,6 +520,7 @@ public sealed class ResourceLinkBlock : ContentBlock
503520
}
504521

505522
/// <summary>Represents a request from the assistant to call a tool.</summary>
523+
[DebuggerDisplay("Name = {Name}, Id = {Id}")]
506524
public sealed class ToolUseContentBlock : ContentBlock
507525
{
508526
/// <inheritdoc/>
@@ -531,6 +549,7 @@ public sealed class ToolUseContentBlock : ContentBlock
531549
}
532550

533551
/// <summary>Represents the result of a tool use, provided by the user back to the assistant.</summary>
552+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
534553
public sealed class ToolResultContentBlock : ContentBlock
535554
{
536555
/// <inheritdoc/>
@@ -575,4 +594,37 @@ public sealed class ToolResultContentBlock : ContentBlock
575594
/// </remarks>
576595
[JsonPropertyName("isError")]
577596
public bool? IsError { get; set; }
597+
598+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
599+
private string DebuggerDisplay
600+
{
601+
get
602+
{
603+
if (IsError == true)
604+
{
605+
return $"ToolUseId = {ToolUseId}, IsError = true";
606+
}
607+
608+
// Try to show the result content
609+
if (Content.Count == 1 && Content[0] is TextContentBlock textBlock)
610+
{
611+
return $"ToolUseId = {ToolUseId}, Result = \"{textBlock.Text}\"";
612+
}
613+
614+
if (StructuredContent.HasValue)
615+
{
616+
try
617+
{
618+
string json = StructuredContent.Value.GetRawText();
619+
return $"ToolUseId = {ToolUseId}, Result = {json}";
620+
}
621+
catch
622+
{
623+
// Fall back to content count if GetRawText fails
624+
}
625+
}
626+
627+
return $"ToolUseId = {ToolUseId}, ContentCount = {Content.Count}";
628+
}
629+
}
578630
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
3+
namespace ModelContextProtocol.Protocol;
4+
5+
/// <summary>
6+
/// Internal helper methods for DebuggerDisplay implementations.
7+
/// </summary>
8+
internal static class DebuggerDisplayHelper
9+
{
10+
/// <summary>
11+
/// Gets the decoded length of base64 data for debugger display.
12+
/// </summary>
13+
internal static string GetBase64LengthDisplay(string base64Data)
14+
{
15+
#if NET
16+
if (System.Buffers.Text.Base64.IsValid(base64Data, out int decodedLength))
17+
{
18+
return $"{decodedLength} bytes";
19+
}
20+
#else
21+
try
22+
{
23+
return $"{Convert.FromBase64String(base64Data).Length} bytes";
24+
}
25+
catch { }
26+
#endif
27+
28+
return "invalid base64";
29+
}
30+
}

src/ModelContextProtocol.Core/Protocol/Icon.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Text.Json.Serialization;
23

34
namespace ModelContextProtocol.Protocol;
@@ -28,6 +29,7 @@ namespace ModelContextProtocol.Protocol;
2829
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for details.
2930
/// </para>
3031
/// </remarks>
32+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
3133
public sealed class Icon
3234
{
3335
/// <summary>
@@ -82,4 +84,14 @@ public sealed class Icon
8284
/// </remarks>
8385
[JsonPropertyName("theme")]
8486
public string? Theme { get; set; }
87+
88+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
89+
private string DebuggerDisplay
90+
{
91+
get
92+
{
93+
string mimeInfo = MimeType is not null ? $", MimeType = {MimeType}" : "";
94+
return $"Source = \"{Source}\"{mimeInfo}";
95+
}
96+
}
8597
}

src/ModelContextProtocol.Core/Protocol/Prompt.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Text.Json.Nodes;
23
using System.Text.Json.Serialization;
34
using ModelContextProtocol.Server;
@@ -10,6 +11,7 @@ namespace ModelContextProtocol.Protocol;
1011
/// <remarks>
1112
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for details.
1213
/// </remarks>
14+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1315
public sealed class Prompt : IBaseMetadata
1416
{
1517
/// <inheritdoc />
@@ -75,4 +77,14 @@ public sealed class Prompt : IBaseMetadata
7577
/// </summary>
7678
[JsonIgnore]
7779
public McpServerPrompt? McpServerPrompt { get; set; }
80+
81+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
82+
private string DebuggerDisplay
83+
{
84+
get
85+
{
86+
string desc = Description is not null ? $", Description = \"{Description}\"" : "";
87+
return $"Name = {Name}{desc}";
88+
}
89+
}
7890
}

src/ModelContextProtocol.Core/Protocol/PromptMessage.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using Microsoft.Extensions.AI;
23
using System.Text.Json.Serialization;
34

@@ -26,6 +27,7 @@ namespace ModelContextProtocol.Protocol;
2627
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for details.
2728
/// </para>
2829
/// </remarks>
30+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
2931
public sealed class PromptMessage
3032
{
3133
/// <summary>
@@ -49,4 +51,19 @@ public sealed class PromptMessage
4951
/// </remarks>
5052
[JsonPropertyName("role")]
5153
public required Role Role { get; set; }
54+
55+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
56+
private string DebuggerDisplay
57+
{
58+
get
59+
{
60+
// Show actual text content if it's a TextContentBlock
61+
if (Content is TextContentBlock textBlock)
62+
{
63+
return $"Role = {Role}, Text = \"{textBlock.Text}\"";
64+
}
65+
66+
return $"Role = {Role}, ContentType = {Content.Type}";
67+
}
68+
}
5269
}

src/ModelContextProtocol.Core/Protocol/Resource.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Diagnostics.CodeAnalysis;
23
using System.Text.Json.Nodes;
34
using System.Text.Json.Serialization;
@@ -11,6 +12,7 @@ namespace ModelContextProtocol.Protocol;
1112
/// <remarks>
1213
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for details.
1314
/// </remarks>
15+
[DebuggerDisplay("Name = {Name}, Uri = {Uri}")]
1416
public sealed class Resource : IBaseMetadata
1517
{
1618
/// <inheritdoc />

src/ModelContextProtocol.Core/Protocol/SamplingMessage.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Text.Json.Nodes;
23
using System.Text.Json.Serialization;
34

@@ -26,6 +27,7 @@ namespace ModelContextProtocol.Protocol;
2627
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for details.
2728
/// </para>
2829
/// </remarks>
30+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
2931
public sealed class SamplingMessage
3032
{
3133
/// <summary>
@@ -49,4 +51,22 @@ public sealed class SamplingMessage
4951
/// </remarks>
5052
[JsonPropertyName("_meta")]
5153
public JsonObject? Meta { get; set; }
54+
55+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
56+
private string DebuggerDisplay
57+
{
58+
get
59+
{
60+
// Show actual text content if it's a single TextContentBlock
61+
if (Content.Count == 1 && Content[0] is TextContentBlock textBlock)
62+
{
63+
return $"Role = {Role}, Text = \"{textBlock.Text}\"";
64+
}
65+
66+
string contentTypes = Content.Count == 1
67+
? Content[0].Type
68+
: $"{Content.Count} items";
69+
return $"Role = {Role}, Content = {contentTypes}";
70+
}
71+
}
5272
}

src/ModelContextProtocol.Core/Protocol/TextResourceContents.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Text.Json.Serialization;
23

34
namespace ModelContextProtocol.Protocol;
@@ -19,11 +20,15 @@ namespace ModelContextProtocol.Protocol;
1920
/// See the <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">schema</see> for more details.
2021
/// </para>
2122
/// </remarks>
23+
[DebuggerDisplay("Uri = \"{Uri}\", Text = \"{Text}\"")]
2224
public sealed class TextResourceContents : ResourceContents
2325
{
2426
/// <summary>
2527
/// Gets or sets the text of the item.
2628
/// </summary>
2729
[JsonPropertyName("text")]
2830
public required string Text { get; set; }
31+
32+
/// <inheritdoc/>
33+
public override string ToString() => Text ?? "";
2934
}

src/ModelContextProtocol.Core/Protocol/Tool.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Text.Json;
23
using System.Text.Json.Nodes;
34
using System.Text.Json.Serialization;
@@ -8,6 +9,7 @@ namespace ModelContextProtocol.Protocol;
89
/// <summary>
910
/// Represents a tool that the server is capable of calling.
1011
/// </summary>
12+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1113
public sealed class Tool : IBaseMetadata
1214
{
1315
/// <inheritdoc />
@@ -130,4 +132,14 @@ public JsonElement? OutputSchema
130132
/// </summary>
131133
[JsonIgnore]
132134
public McpServerTool? McpServerTool { get; set; }
135+
136+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
137+
private string DebuggerDisplay
138+
{
139+
get
140+
{
141+
string desc = Description is not null ? $", Description = \"{Description}\"" : "";
142+
return $"Name = {Name}{desc}";
143+
}
144+
}
133145
}

0 commit comments

Comments
 (0)