Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Augment McpServerTool/Prompt docs with details of parameter/result marshaling #271

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 111 additions & 1 deletion src/ModelContextProtocol/Server/McpServerPrompt.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,121 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Types;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;

namespace ModelContextProtocol.Server;

/// <summary>Represents an invocable prompt used by Model Context Protocol servers.</summary>
/// <summary>
/// Represents an invocable prompt used by Model Context Protocol clients and servers.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="McpServerPrompt"/> is an abstract base class that represents an MCP prompt for use in the server (as opposed
/// to <see cref="Prompt"/>, which provides the protocol representation of a prompt, and <see cref="McpClientPrompt"/>, which
/// provides a client-side representation of a prompt). Instances of <see cref="McpServerPrompt"/> can be added into a
/// <see cref="IServiceCollection"/> to be picked up automatically when <see cref="McpServerFactory"/> is used to create
/// an <see cref="IMcpServer"/>, or added into a <see cref="McpServerPrimitiveCollection{McpServerPrompt}"/> to be used as
/// a <see cref="PromptsCapability.PromptCollection"/>.
/// </para>
/// <para>
/// Most commonly, <see cref="McpServerPrompt"/> instances are created using the static <see cref="M:McpServerPrompt.Create"/> methods.
/// These methods enable creating an <see cref="McpServerPrompt"/> for a method, specified via a <see cref="Delegate"/> or
/// <see cref="MethodInfo"/>, and are what are used implicitly by <see cref="McpServerBuilderExtensions.WithPromptsFromAssembly"/> and
/// <see cref="M:McpServerBuilderExtensions.WithPrompts"/>. The <see cref="M:McpServerPrompt.Create"/> methods
/// create <see cref="McpServerPrompt"/> instances capable of working with a large variety of .NET method signatures, automatically handling
/// how parameters are marshaled into the method from the JSON received from the MCP client, and how the return value is marshaled back
/// into the <see cref="GetPromptResult"/> that's then serialized and sent back to the client.
/// </para>
/// <para>
/// By default, parameters are sourced from the <see cref="GetPromptRequestParams.Arguments"/> dictionary, which is a collection
/// of key/value pairs. Those parameters are deserialized from the
/// <see cref="JsonElement"/> values in that collection. There are a few exceptions to this:
/// <list type="bullet">
/// <item>
/// <description>
/// <see cref="CancellationToken"/> parameters are automatically bound to a <see cref="CancellationToken"/> provided by the
/// <see cref="IMcpServer"/> and that respects any <see cref="CancelledNotification"/>s sent by the client for this operation's
/// <see cref="RequestId"/>.
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="IServiceProvider"/> parameters are bound from the <see cref="RequestContext{GetPromptRequestParams}"/> for this request.
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="IMcpServer"/> parameters are bound directly to the <see cref="IMcpServer"/> instance associated
/// with this request's <see cref="RequestContext{CallPromptRequestParams}"/>. Such parameters may be used to understand
/// what server is being used to process the request, and to interact with the client issuing the request to that server.
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="IProgress{ProgressNotificationValue}"/> parameters accepting <see cref="ProgressNotificationValue"/> values
/// are bound to an <see cref="IProgress{ProgressNotificationValue}"/> instance manufactured to forward progress notifications
/// from the prompt to the client. If the client included a <see cref="ProgressToken"/> in their request, progress reports issued
/// to this instance will propagate to the client as <see cref="NotificationMethods.ProgressNotification"/> notifications with
/// that token. If the client did not include a <see cref="ProgressToken"/>, the instance will ignore any progress reports issued to it.
/// </description>
/// </item>
/// <item>
/// <description>
/// When the <see cref="McpServerPrompt"/> is constructed, it may be passed an <see cref="IServiceProvider"/> via
/// <see cref="McpServerPromptCreateOptions.Services"/>. Any parameter that can be satisfied by that <see cref="IServiceProvider"/>
/// will be resolved from the <see cref="IServiceProvider"/> provided to <see cref="GetAsync"/> rather than from the argument collection.
/// </description>
/// </item>
/// <item>
/// <description>
/// Any parameter attributed with <see cref="FromKeyedServicesAttribute"/> will similarly be resolved from the
/// <see cref="IServiceProvider"/> provided to <see cref="GetAsync"/> rather than from the argument collection.
/// </description>
/// </item>
/// </list>
/// </para>
/// <para>
/// All other parameters are deserialized from the <see cref="JsonElement"/>s in the <see cref="GetPromptRequestParams.Arguments"/> dictionary.
/// </para>
/// <para>
/// In general, the data supplied via the <see cref="GetPromptRequestParams.Arguments"/>'s dictionary is passed along from the caller and
/// should thus be considered unvalidated and untrusted. To provide validated and trusted data to the invocation of the prompt, consider having
/// the prompt be an instance method, referring to data stored in the instance, or using an instance or parameters resolved from the <see cref="IServiceProvider"/>
/// to provide data to the method.
/// </para>
/// <para>
/// Return values from a method are used to create the <see cref="GetPromptResult"/> that is sent back to the client:
/// </para>
/// <list type="table">
/// <item>
/// <term><see cref="string"/></term>
/// <description>Converted to a list containing a single <see cref="PromptMessage"/> with its <see cref="PromptMessage.Content"/> set to contain the <see cref="string"/>.</description>
/// </item>
/// <item>
/// <term><see cref="PromptMessage"/></term>
/// <description>Converted to a list containing the single <see cref="PromptMessage"/>.</description>
/// </item>
/// <item>
/// <term><see cref="IEnumerable{PromptMessage}"/> of <see cref="PromptMessage"/></term>
/// <description>Converted to a list containing all of the returned <see cref="PromptMessage"/> instances.</description>
/// </item>
/// <item>
/// <term><see cref="ChatMessage"/></term>
/// <description>Converted to a list of <see cref="PromptMessage"/> instances derived from the <see cref="ChatMessage"/> with <see cref="AIContentExtensions.ToPromptMessages"/>.</description>
/// </item>
/// <item>
/// <term><see cref="IEnumerable{PromptMessage}"/> of <see cref="PromptMessage"/></term>
/// <description>Converted to a list of <see cref="PromptMessage"/> instances derived from all of the <see cref="ChatMessage"/> instances with <see cref="AIContentExtensions.ToPromptMessages"/>.</description>
/// </item>
/// </list>
/// <para>
/// Other returned types will result in an <see cref="InvalidOperationException"/> being thrown.
/// </para>
/// </remarks>
public abstract class McpServerPrompt : IMcpServerPrimitive
{
/// <summary>Initializes a new instance of the <see cref="McpServerPrompt"/> class.</summary>
Expand Down
89 changes: 89 additions & 0 deletions src/ModelContextProtocol/Server/McpServerPromptAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Types;
using System.Text.Json;

namespace ModelContextProtocol.Server;

Expand All @@ -14,6 +18,91 @@ namespace ModelContextProtocol.Server;
/// <para>
/// When methods are provided directly to <see cref="M:McpServerPrompt.Create"/>, the attribute is not required.
/// </para>
/// <para>
/// By default, parameters are sourced from the <see cref="GetPromptRequestParams.Arguments"/> dictionary, which is a collection
/// of key/value pairs. Those parameters are deserialized from the
/// <see cref="JsonElement"/> values in that collection. There are a few exceptions to this:
/// <list type="bullet">
/// <item>
/// <description>
/// <see cref="CancellationToken"/> parameters are automatically bound to a <see cref="CancellationToken"/> provided by the
/// <see cref="IMcpServer"/> and that respects any <see cref="CancelledNotification"/>s sent by the client for this operation's
/// <see cref="RequestId"/>.
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="IServiceProvider"/> parameters are bound from the <see cref="RequestContext{GetPromptRequestParams}"/> for this request.
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="IMcpServer"/> parameters are bound directly to the <see cref="IMcpServer"/> instance associated
/// with this request's <see cref="RequestContext{CallPromptRequestParams}"/>. Such parameters may be used to understand
/// what server is being used to process the request, and to interact with the client issuing the request to that server.
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="IProgress{ProgressNotificationValue}"/> parameters accepting <see cref="ProgressNotificationValue"/> values
/// are bound to an <see cref="IProgress{ProgressNotificationValue}"/> instance manufactured to forward progress notifications
/// from the prompt to the client. If the client included a <see cref="ProgressToken"/> in their request, progress reports issued
/// to this instance will propagate to the client as <see cref="NotificationMethods.ProgressNotification"/> notifications with
/// that token. If the client did not include a <see cref="ProgressToken"/>, the instance will ignore any progress reports issued to it.
/// </description>
/// </item>
/// <item>
/// <description>
/// When the <see cref="McpServerPrompt"/> is constructed, it may be passed an <see cref="IServiceProvider"/> via
/// <see cref="McpServerPromptCreateOptions.Services"/>. Any parameter that can be satisfied by that <see cref="IServiceProvider"/>
/// will be resolved from the <see cref="IServiceProvider"/> provided to the prompt invocation rather than from the argument collection.
/// </description>
/// </item>
/// <item>
/// <description>
/// Any parameter attributed with <see cref="FromKeyedServicesAttribute"/> will similarly be resolved from the
/// <see cref="IServiceProvider"/> provided to the prompt invocation rather than from the argument collection.
/// </description>
/// </item>
/// </list>
/// </para>
/// <para>
/// All other parameters are deserialized from the <see cref="JsonElement"/>s in the <see cref="GetPromptRequestParams.Arguments"/> dictionary.
/// </para>
/// <para>
/// In general, the data supplied via the <see cref="GetPromptRequestParams.Arguments"/>'s dictionary is passed along from the caller and
/// should thus be considered unvalidated and untrusted. To provide validated and trusted data to the invocation of the prompt, consider having
/// the prompt be an instance method, referring to data stored in the instance, or using an instance or parameters resolved from the <see cref="IServiceProvider"/>
/// to provide data to the method.
/// </para>
/// <para>
/// Return values from a method are used to create the <see cref="GetPromptResult"/> that is sent back to the client:
/// </para>
/// <list type="table">
/// <item>
/// <term><see cref="string"/></term>
/// <description>Converted to a list containing a single <see cref="PromptMessage"/> with its <see cref="PromptMessage.Content"/> set to contain the <see cref="string"/>.</description>
/// </item>
/// <item>
/// <term><see cref="PromptMessage"/></term>
/// <description>Converted to a list containing the single <see cref="PromptMessage"/>.</description>
/// </item>
/// <item>
/// <term><see cref="IEnumerable{PromptMessage}"/> of <see cref="PromptMessage"/></term>
/// <description>Converted to a list containing all of the returned <see cref="PromptMessage"/> instances.</description>
/// </item>
/// <item>
/// <term><see cref="ChatMessage"/></term>
/// <description>Converted to a list of <see cref="PromptMessage"/> instances derived from the <see cref="ChatMessage"/> with <see cref="AIContentExtensions.ToPromptMessages"/>.</description>
/// </item>
/// <item>
/// <term><see cref="IEnumerable{PromptMessage}"/> of <see cref="PromptMessage"/></term>
/// <description>Converted to a list of <see cref="PromptMessage"/> instances derived from all of the <see cref="ChatMessage"/> instances with <see cref="AIContentExtensions.ToPromptMessages"/>.</description>
/// </item>
/// </list>
/// <para>
/// Other returned types will result in an <see cref="InvalidOperationException"/> being thrown.
/// </para>
/// </remarks>
[AttributeUsage(AttributeTargets.Method)]
public sealed class McpServerPromptAttribute : Attribute
Expand Down
Loading