Sampling capability (#7)
This commit is contained in:
parent
211cd2175b
commit
8c6dfd53c8
14 changed files with 370 additions and 9 deletions
|
|
@ -341,4 +341,69 @@ public class IntegrationTests
|
||||||
Assert.Single(result.Contents);
|
Assert.Single(result.Contents);
|
||||||
Assert.NotNull(result.Contents[0].Blob);
|
Assert.NotNull(result.Contents[0].Blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Sampling_Stdio_EverythingServer()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var config = new McpServerConfig
|
||||||
|
{
|
||||||
|
Id = "everything",
|
||||||
|
Name = "Everything",
|
||||||
|
TransportType = "stdio",
|
||||||
|
TransportOptions = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["command"] = "npx",
|
||||||
|
["arguments"] = "-y @modelcontextprotocol/server-everything"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var options = new McpClientOptions
|
||||||
|
{
|
||||||
|
ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" },
|
||||||
|
Capabilities = new ClientCapabilities
|
||||||
|
{
|
||||||
|
Sampling = new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var factory = new McpClientFactory(
|
||||||
|
new[] { config },
|
||||||
|
options
|
||||||
|
);
|
||||||
|
var client = await factory.GetClientAsync("everything");
|
||||||
|
|
||||||
|
// Set up the sampling handler
|
||||||
|
int samplingHandlerCalls = 0;
|
||||||
|
client.SamplingHandler = async (_) =>
|
||||||
|
{
|
||||||
|
samplingHandlerCalls++;
|
||||||
|
return new CreateMessageResult
|
||||||
|
{
|
||||||
|
Model = "test-model",
|
||||||
|
Role = "assistant",
|
||||||
|
Content = new Content
|
||||||
|
{
|
||||||
|
Type = "text",
|
||||||
|
Text = "Test response"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the server's sampleLLM tool which should trigger our sampling handler
|
||||||
|
var result = await client.CallToolAsync(
|
||||||
|
"sampleLLM",
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["prompt"] = "Test prompt",
|
||||||
|
["maxTokens"] = 100
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
var textContent = Assert.Single(result.Content);
|
||||||
|
Assert.Equal("text", textContent.Type);
|
||||||
|
Assert.Contains("Test response", textContent.Text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ public interface IMcpClient : IAsyncDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Implementation? ServerInfo { get; }
|
Implementation? ServerInfo { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the handler for the server's sampling requests.
|
||||||
|
/// </summary>
|
||||||
|
Func<CreateMessageRequestParams, Task<CreateMessageResult>>? SamplingHandler { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Establishes a connection to the server.
|
/// Establishes a connection to the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ using McpDotNet.Protocol.Messages;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using McpDotNet.Utils.Json;
|
using McpDotNet.Utils.Json;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
internal class McpClient : IMcpClient
|
internal class McpClient : IMcpClient
|
||||||
|
|
@ -15,6 +17,7 @@ internal class McpClient : IMcpClient
|
||||||
private readonly McpClientOptions _options;
|
private readonly McpClientOptions _options;
|
||||||
private readonly ConcurrentDictionary<RequestId, TaskCompletionSource<IJsonRpcMessage>> _pendingRequests;
|
private readonly ConcurrentDictionary<RequestId, TaskCompletionSource<IJsonRpcMessage>> _pendingRequests;
|
||||||
private readonly ConcurrentDictionary<string, List<Func<JsonRpcNotification,Task>>> _notificationHandlers;
|
private readonly ConcurrentDictionary<string, List<Func<JsonRpcNotification,Task>>> _notificationHandlers;
|
||||||
|
private readonly Dictionary<string, Func<JsonRpcRequest, Task<object>>> _requestHandlers = new();
|
||||||
private int _nextRequestId;
|
private int _nextRequestId;
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
private Task? _messageProcessingTask;
|
private Task? _messageProcessingTask;
|
||||||
|
|
@ -42,6 +45,17 @@ internal class McpClient : IMcpClient
|
||||||
_pendingRequests = new();
|
_pendingRequests = new();
|
||||||
_notificationHandlers = new();
|
_notificationHandlers = new();
|
||||||
_nextRequestId = 1;
|
_nextRequestId = 1;
|
||||||
|
|
||||||
|
if (options.Capabilities?.Sampling != null)
|
||||||
|
{
|
||||||
|
SetRequestHandler<CreateMessageRequestParams, CreateMessageResult>("sampling/createMessage",
|
||||||
|
async (request) => {
|
||||||
|
if (SamplingHandler == null)
|
||||||
|
throw new McpClientException("Sampling handler not configured");
|
||||||
|
|
||||||
|
return await SamplingHandler(request);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -271,7 +285,7 @@ internal class McpClient : IMcpClient
|
||||||
{
|
{
|
||||||
await foreach (var message in _transport.MessageReader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
|
await foreach (var message in _transport.MessageReader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
await HandleMessageAsync(message).ConfigureAwait(false);
|
await HandleMessageAsync(message, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||||
|
|
@ -284,10 +298,39 @@ internal class McpClient : IMcpClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleMessageAsync(IJsonRpcMessage message)
|
private async Task HandleMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
switch (message)
|
switch (message)
|
||||||
{
|
{
|
||||||
|
case JsonRpcRequest request:
|
||||||
|
if (_requestHandlers.TryGetValue(request.Method, out var handler))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await handler(request);
|
||||||
|
await _transport.SendMessageAsync(new JsonRpcResponse
|
||||||
|
{
|
||||||
|
Id = request.Id,
|
||||||
|
JsonRpc = "2.0",
|
||||||
|
Result = result
|
||||||
|
}, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Send error response
|
||||||
|
await _transport.SendMessageAsync(new JsonRpcError
|
||||||
|
{
|
||||||
|
Id = request.Id,
|
||||||
|
JsonRpc = "2.0",
|
||||||
|
Error = new JsonRpcErrorDetail
|
||||||
|
{
|
||||||
|
Code = -32000, // Implementation defined error
|
||||||
|
Message = ex.Message
|
||||||
|
}
|
||||||
|
}, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case IJsonRpcMessageWithId messageWithId:
|
case IJsonRpcMessageWithId messageWithId:
|
||||||
if (_pendingRequests.TryRemove(messageWithId.Id, out var tcs))
|
if (_pendingRequests.TryRemove(messageWithId.Id, out var tcs))
|
||||||
{
|
{
|
||||||
|
|
@ -298,11 +341,11 @@ internal class McpClient : IMcpClient
|
||||||
case JsonRpcNotification notification:
|
case JsonRpcNotification notification:
|
||||||
if (_notificationHandlers.TryGetValue(notification.Method, out var handlers))
|
if (_notificationHandlers.TryGetValue(notification.Method, out var handlers))
|
||||||
{
|
{
|
||||||
foreach (var handler in handlers)
|
foreach (var notificationHandler in handlers)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await handler(notification).ConfigureAwait(false);
|
await notificationHandler(notification).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -383,6 +426,18 @@ internal class McpClient : IMcpClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Func<CreateMessageRequestParams, Task<CreateMessageResult>>? SamplingHandler { get; set; }
|
||||||
|
|
||||||
|
private void SetRequestHandler<TRequest, TResponse>(string method, Func<TRequest, Task<TResponse>> handler)
|
||||||
|
{
|
||||||
|
_requestHandlers[method] = async (request) => {
|
||||||
|
// Convert the params JsonElement to our type using the same options
|
||||||
|
var jsonString = JsonSerializer.Serialize(request.Params);
|
||||||
|
var typedRequest = JsonSerializer.Deserialize<TRequest>(jsonString, _jsonOptions);
|
||||||
|
return await handler(typedRequest);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async Task CleanupAsync()
|
private async Task CleanupAsync()
|
||||||
{
|
{
|
||||||
_cts?.Cancel();
|
_cts?.Cancel();
|
||||||
|
|
@ -419,3 +474,4 @@ internal class McpClient : IMcpClient
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
namespace McpDotNet.Protocol.Messages;
|
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace McpDotNet.Protocol.Messages;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A successful response message in the JSON-RPC protocol.
|
/// A successful response message in the JSON-RPC protocol.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,7 @@ public sealed class StdioTransport : TransportBase
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = JsonSerializer.Serialize(message, _jsonOptions);
|
var json = JsonSerializer.Serialize(message, _jsonOptions);
|
||||||
|
Debug.WriteLine($"Sending message: {json}");
|
||||||
|
|
||||||
// Write the message followed by a newline
|
// Write the message followed by a newline
|
||||||
await _process!.StandardInput.WriteLineAsync(json.AsMemory(), cancellationToken);
|
await _process!.StandardInput.WriteLineAsync(json.AsMemory(), cancellationToken);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
namespace McpDotNet.Protocol.Types;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the content of a tool response.
|
/// Represents the content of a tool response.
|
||||||
|
|
@ -10,26 +12,32 @@ public class Content
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of content. This determines the structure of the content object. Can be "image", "text", "resource".
|
/// The type of content. This determines the structure of the content object. Can be "image", "text", "resource".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
|
[JsonPropertyName("type")]
|
||||||
public string Type { get; set; } = string.Empty;
|
public string Type { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The text content of the message.
|
/// The text content of the message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonPropertyName("text")]
|
||||||
public string? Text { get; set; }
|
public string? Text { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base64-encoded image data.
|
/// The base64-encoded image data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonPropertyName("data")]
|
||||||
public string? Data { get; set; }
|
public string? Data { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The MIME type of the image.
|
/// The MIME type of the image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonPropertyName("mime_type")]
|
||||||
public string? MimeType { get; set; }
|
public string? MimeType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The resource content of the message (if embedded).
|
/// The resource content of the message (if embedded).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonPropertyName("resource")]
|
||||||
public ResourceContents? Resource { get; set; }
|
public ResourceContents? Resource { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
11
src/mcpdotnet/Protocol/Types/ContextInclusion.cs
Normal file
11
src/mcpdotnet/Protocol/Types/ContextInclusion.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A request to include context from one or more MCP servers (including the caller), to be attached to the prompt.
|
||||||
|
/// </summary>
|
||||||
|
public enum ContextInclusion
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
ThisServer,
|
||||||
|
AllServers
|
||||||
|
}
|
||||||
52
src/mcpdotnet/Protocol/Types/CreateMessageRequestParams.cs
Normal file
52
src/mcpdotnet/Protocol/Types/CreateMessageRequestParams.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
namespace McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A request from the server to sample an LLM via the client.
|
||||||
|
/// The client has full discretion over which model to select.
|
||||||
|
/// The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it.
|
||||||
|
///
|
||||||
|
/// While these align with the protocol specification,
|
||||||
|
/// clients have full discretion over model selection and should inform users before sampling.
|
||||||
|
/// </summary>
|
||||||
|
public class CreateMessageRequestParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. The client MAY ignore this request.
|
||||||
|
/// </summary>
|
||||||
|
public ContextInclusion? IncludeContext { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of tokens to sample, as requested by the server. The client MAY choose to sample fewer tokens than requested.
|
||||||
|
/// </summary>
|
||||||
|
public int MaxTokens { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Messages requested by the server to be included in the prompt.
|
||||||
|
/// </summary>
|
||||||
|
public required IReadOnlyList<SamplingMessage> Messages { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific.
|
||||||
|
/// </summary>
|
||||||
|
public object? Metadata { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The server's preferences for which model to select. The client MAY ignore these preferences.
|
||||||
|
/// </summary>
|
||||||
|
public ModelPreferences? ModelPreferences { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional stop sequences that the server wants to use for sampling.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<string>? StopSequences { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt.
|
||||||
|
/// </summary>
|
||||||
|
public string? SystemPrompt { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The temperature to use for sampling, as requested by the server.
|
||||||
|
/// </summary>
|
||||||
|
public float? Temperature { get; init; }
|
||||||
|
}
|
||||||
35
src/mcpdotnet/Protocol/Types/CreateMessageResult.cs
Normal file
35
src/mcpdotnet/Protocol/Types/CreateMessageResult.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The client's response to a sampling/create_message request from the server.
|
||||||
|
/// The client should inform the user before returning the sampled message, to allow them to inspect the response (human in the loop)
|
||||||
|
/// and decide whether to allow the server to see it.
|
||||||
|
/// </summary>
|
||||||
|
public class CreateMessageResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Text or image content of the message.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("content")]
|
||||||
|
public required Content Content { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the model that generated the message.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("model")]
|
||||||
|
public required string Model { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The reason why sampling stopped, if known.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("stop_reason")]
|
||||||
|
public string? StopReason { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The role of the user who generated the message.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("role")]
|
||||||
|
public required string Role { get; init; }
|
||||||
|
}
|
||||||
22
src/mcpdotnet/Protocol/Types/ModelHint.cs
Normal file
22
src/mcpdotnet/Protocol/Types/ModelHint.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hints to use for model selection.
|
||||||
|
/// Keys not declared here are currently left unspecified by the spec and are up
|
||||||
|
/// to the client to interpret.
|
||||||
|
/// </summary>
|
||||||
|
public class ModelHint
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A hint for a model name.
|
||||||
|
///
|
||||||
|
/// The client SHOULD treat this as a substring of a model name; for example:
|
||||||
|
/// - `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022`
|
||||||
|
/// - `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc.
|
||||||
|
/// - `claude` should match any Claude model
|
||||||
|
///
|
||||||
|
/// The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example:
|
||||||
|
/// - `gemini-1.5-flash` could match `claude-3-haiku-20240307`
|
||||||
|
/// </summary>
|
||||||
|
public string? Name { get; init; }
|
||||||
|
}
|
||||||
82
src/mcpdotnet/Protocol/Types/ModelPreferences.cs
Normal file
82
src/mcpdotnet/Protocol/Types/ModelPreferences.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
namespace McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The server's preferences for model selection, requested of the client during sampling.
|
||||||
|
/// Because LLMs can vary along multiple dimensions, choosing the \"best\" model is
|
||||||
|
/// rarely straightforward. Different models excel in different areas—some are
|
||||||
|
/// faster but less capable, others are more capable but more expensive, and so
|
||||||
|
/// on. This interface allows servers to express their priorities across multiple
|
||||||
|
/// dimensions to help clients make an appropriate selection for their use case.
|
||||||
|
///
|
||||||
|
/// These preferences are always advisory. The client MAY ignore them. It is also
|
||||||
|
/// up to the client to decide how to interpret these preferences and how to
|
||||||
|
/// balance them against other considerations.
|
||||||
|
/// </summary>
|
||||||
|
public class ModelPreferences
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// How much to prioritize cost when selecting a model. A value of 0 means cost\nis not important, while a value of 1 means cost is the most important
|
||||||
|
/// factor.
|
||||||
|
/// </summary>
|
||||||
|
public float? CostPriority { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional hints to use for model selection.
|
||||||
|
///
|
||||||
|
/// If multiple hints are specified, the client MUST evaluate them in order
|
||||||
|
/// (such that the first match is taken).
|
||||||
|
///
|
||||||
|
/// The client SHOULD prioritize these hints over the numeric priorities, but
|
||||||
|
/// MAY still use the priorities to select from ambiguous matches.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<ModelHint>? Hints { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much to prioritize intelligence and capabilities when selecting a
|
||||||
|
/// model. A value of 0 means intelligence is not important, while a value of 1
|
||||||
|
/// means intelligence is the most important factor.
|
||||||
|
/// </summary>
|
||||||
|
public float? SpeedPriority { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much to prioritize sampling speed (latency) when selecting a model. A
|
||||||
|
/// value of 0 means speed is not important, while a value of 1 means speed is
|
||||||
|
/// the most important factor.
|
||||||
|
/// </summary>
|
||||||
|
public float? IntelligencePriority { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the model preferences.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorMessage">Error message if object isn't valid</param>
|
||||||
|
/// <returns>True if valid, false if invalid</returns>
|
||||||
|
public bool Validate(out string errorMessage)
|
||||||
|
{
|
||||||
|
bool valid = true;
|
||||||
|
List<string> errors = new();
|
||||||
|
if (CostPriority < 0 || CostPriority > 1)
|
||||||
|
{
|
||||||
|
errors.Add("CostPriority must be between 0 and 1");
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (SpeedPriority < 0 || SpeedPriority > 1)
|
||||||
|
{
|
||||||
|
errors.Add("SpeedPriority must be between 0 and 1");
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (IntelligencePriority < 0 || IntelligencePriority > 1)
|
||||||
|
{
|
||||||
|
errors.Add("IntelligencePriority must be between 0 and 1");
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
errorMessage = string.Join(", ", errors);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = "";
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
namespace McpDotNet.Protocol.Types;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the content of a resource.
|
/// Represents the content of a resource.
|
||||||
|
|
@ -8,21 +10,25 @@ public class ResourceContents
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The URI of the resource.
|
/// The URI of the resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonPropertyName("uri")]
|
||||||
public string Uri { get; set; } = string.Empty;
|
public string Uri { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of content.
|
/// The type of content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonPropertyName("mime_type")]
|
||||||
public string? MimeType { get; set; }
|
public string? MimeType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The text content of the resource.
|
/// The text content of the resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonPropertyName("text")]
|
||||||
public string? Text { get; set; }
|
public string? Text { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base64-encoded binary content of the resource.
|
/// The base64-encoded binary content of the resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonPropertyName("blob")]
|
||||||
public string? Blob { get; set; }
|
public string? Blob { get; set; }
|
||||||
}
|
}
|
||||||
18
src/mcpdotnet/Protocol/Types/SamplingMessage.cs
Normal file
18
src/mcpdotnet/Protocol/Types/SamplingMessage.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
namespace McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a message issued to or received from an LLM API.
|
||||||
|
/// </summary>
|
||||||
|
public class SamplingMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Text or image content of the message.
|
||||||
|
/// </summary>
|
||||||
|
public required Content Content { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The role of the message ("user" or "assistant").
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
public required Role Role { get; init; } // "user" or "assistant"
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- license and package properties -->
|
<!-- license and package properties -->
|
||||||
<PackageId>mcpdotnet</PackageId>
|
<PackageId>mcpdotnet</PackageId>
|
||||||
<Version>0.2.0-alpha1</Version>
|
<Version>0.3.0.1</Version>
|
||||||
<Authors>PederHP</Authors>
|
<Authors>PederHP</Authors>
|
||||||
<Description>.NET client library for the Model Context Protocol (MCP)</Description>
|
<Description>.NET client library for the Model Context Protocol (MCP)</Description>
|
||||||
<PackageProjectUrl>https://github.com/PederHP/mcpdotnet</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/PederHP/mcpdotnet</PackageProjectUrl>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue