Skip to content

Commit 9fb01c5

Browse files
Copilotstephentoub
andcommitted
Add McpMetaAttribute and integrate with tools, prompts, and resources
Co-authored-by: stephentoub <[email protected]>
1 parent 6fb33f6 commit 9fb01c5

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerPrompt.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
138138
Icons = options?.Icons,
139139
};
140140

141+
// Populate Meta from McpMetaAttribute instances if a MethodInfo is in the metadata
142+
if (options?.Metadata?.FirstOrDefault(m => m is MethodInfo) is MethodInfo method)
143+
{
144+
prompt.Meta = AIFunctionMcpServerTool.CreateMetaFromAttributes(method);
145+
}
146+
141147
return new AIFunctionMcpServerPrompt(function, prompt, options?.Metadata ?? []);
142148
}
143149

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
211211

212212
string name = options?.Name ?? function.Name;
213213

214+
// Populate Meta from McpMetaAttribute instances if a MethodInfo is in the metadata
215+
JsonObject? meta = null;
216+
if (options?.Metadata?.FirstOrDefault(m => m is MethodInfo) is MethodInfo method)
217+
{
218+
meta = AIFunctionMcpServerTool.CreateMetaFromAttributes(method);
219+
}
220+
214221
ResourceTemplate resource = new()
215222
{
216223
UriTemplate = options?.UriTemplate ?? DeriveUriTemplate(name, function),
@@ -219,6 +226,7 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
219226
Description = options?.Description,
220227
MimeType = options?.MimeType ?? "application/octet-stream",
221228
Icons = options?.Icons,
229+
Meta = meta,
222230
};
223231

224232
return new AIFunctionMcpServerResource(function, resource, options?.Metadata ?? []);

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ options.OpenWorld is not null ||
143143
ReadOnlyHint = options.ReadOnly,
144144
};
145145
}
146+
147+
// Populate Meta from McpMetaAttribute instances if a MethodInfo is in the metadata
148+
if (options.Metadata?.FirstOrDefault(m => m is MethodInfo) is MethodInfo method)
149+
{
150+
tool.Meta = CreateMetaFromAttributes(method);
151+
}
146152
}
147153

148154
return new AIFunctionMcpServerTool(function, tool, options?.Services, structuredOutputRequiresWrapping, options?.Metadata ?? []);
@@ -351,6 +357,22 @@ internal static IReadOnlyList<object> CreateMetadata(MethodInfo method)
351357
return metadata.AsReadOnly();
352358
}
353359

360+
/// <summary>Creates a Meta JsonObject from McpMetaAttribute instances on the specified method.</summary>
361+
internal static JsonObject? CreateMetaFromAttributes(MethodInfo method)
362+
{
363+
// Get all McpMetaAttribute instances from the method
364+
var metaAttributes = method.GetCustomAttributes<McpMetaAttribute>();
365+
366+
JsonObject? meta = null;
367+
foreach (var attr in metaAttributes)
368+
{
369+
meta ??= new JsonObject();
370+
meta[attr.Name] = attr.Value;
371+
}
372+
373+
return meta;
374+
}
375+
354376
/// <summary>Regex that flags runs of characters other than ASCII digits or letters.</summary>
355377
#if NET
356378
[GeneratedRegex("[^0-9A-Za-z]+")]
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using ModelContextProtocol.Protocol;
2+
3+
namespace ModelContextProtocol.Server;
4+
5+
/// <summary>
6+
/// Used to specify metadata for an MCP server primitive (tool, prompt, or resource).
7+
/// </summary>
8+
/// <remarks>
9+
/// <para>
10+
/// This attribute can be applied multiple times to a method to specify multiple key/value pairs
11+
/// of metadata. The metadata is used to populate the <see cref="Tool.Meta"/>, <see cref="Prompt.Meta"/>,
12+
/// or <see cref="Resource.Meta"/> property of the corresponding primitive.
13+
/// </para>
14+
/// <para>
15+
/// Metadata can be used to attach additional information to primitives, such as model preferences,
16+
/// version information, or other custom data that should be communicated to MCP clients.
17+
/// </para>
18+
/// <example>
19+
/// <code>
20+
/// [McpServerTool]
21+
/// [McpMeta(Name = "model", Value = "gpt-4o")]
22+
/// [McpMeta(Name = "version", Value = "1.0")]
23+
/// public string MyTool(string input)
24+
/// {
25+
/// return $"Processed: {input}";
26+
/// }
27+
/// </code>
28+
/// </example>
29+
/// </remarks>
30+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
31+
public sealed class McpMetaAttribute : Attribute
32+
{
33+
/// <summary>
34+
/// Initializes a new instance of the <see cref="McpMetaAttribute"/> class.
35+
/// </summary>
36+
public McpMetaAttribute()
37+
{
38+
}
39+
40+
/// <summary>
41+
/// Gets or sets the name (key) of the metadata entry.
42+
/// </summary>
43+
/// <remarks>
44+
/// This value is used as the key in the metadata object. It should be a unique identifier
45+
/// for this piece of metadata within the context of the primitive.
46+
/// </remarks>
47+
public required string Name { get; set; }
48+
49+
/// <summary>
50+
/// Gets or sets the value of the metadata entry.
51+
/// </summary>
52+
/// <remarks>
53+
/// This value is stored as a string in the metadata object. Complex values should be
54+
/// serialized to JSON strings if needed.
55+
/// </remarks>
56+
public required string Value { get; set; }
57+
}

0 commit comments

Comments
 (0)