From aae77b1f50a142c2d752a87c228b9c869c2231fc Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Sat, 7 Mar 2026 16:56:11 -0800
Subject: [PATCH 001/182] Update documentation URLs to new vanity domain
(#1414)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
---
.../skills/bump-version/references/semver-assessment.md | 2 +-
.github/skills/prepare-release/SKILL.md | 4 ++--
.github/skills/publish-release/SKILL.md | 2 +-
README.md | 8 ++++----
src/Directory.Build.props | 2 +-
src/PACKAGE.md | 8 ++++----
6 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/.github/skills/bump-version/references/semver-assessment.md b/.github/skills/bump-version/references/semver-assessment.md
index 5a8bf13..2d39ec1 100644
--- a/.github/skills/bump-version/references/semver-assessment.md
+++ b/.github/skills/bump-version/references/semver-assessment.md
@@ -4,7 +4,7 @@ This reference describes how to assess the appropriate [Semantic Versioning 2.0.
## SemVer Summary
-The SDK follows SemVer 2.0.0 as documented in the [C# SDK Versioning](https://modelcontextprotocol.github.io/csharp-sdk/versioning.html) documentation. Given a version `MAJOR.MINOR.PATCH`:
+The SDK follows SemVer 2.0.0 as documented in the [C# SDK Versioning](https://csharp.sdk.modelcontextprotocol.io/versioning.html) documentation. Given a version `MAJOR.MINOR.PATCH`:
- **MAJOR**: Increment when incompatible API changes are included
- **MINOR**: Increment when functionality is added in a backward-compatible manner
diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md
index 45c28b6..4843205 100644
--- a/.github/skills/prepare-release/SKILL.md
+++ b/.github/skills/prepare-release/SKILL.md
@@ -214,7 +214,7 @@ The PR description combines release notes, ApiCompat, and ApiDiff into a single
### Breaking Changes
-Refer to the [C# SDK Versioning](https://modelcontextprotocol.github.io/csharp-sdk/versioning.html) documentation for details on versioning and breaking change policies.
+Refer to the [C# SDK Versioning](https://csharp.sdk.modelcontextprotocol.io/versioning.html) documentation for details on versioning and breaking change policies.
1. **Description #PR**
* Detail of the break
@@ -275,7 +275,7 @@ Omit empty sections. The preamble is **always required** — it is not inside a
## Breaking Changes
-Refer to the [C# SDK Versioning](https://modelcontextprotocol.github.io/csharp-sdk/versioning.html) documentation for details on versioning and breaking change policies.
+Refer to the [C# SDK Versioning](https://csharp.sdk.modelcontextprotocol.io/versioning.html) documentation for details on versioning and breaking change policies.
1. **Description #PR**
* Detail of the break
diff --git a/.github/skills/publish-release/SKILL.md b/.github/skills/publish-release/SKILL.md
index 54f00a5..de11958 100644
--- a/.github/skills/publish-release/SKILL.md
+++ b/.github/skills/publish-release/SKILL.md
@@ -138,7 +138,7 @@ Omit empty sections. The preamble is **always required** — it is not inside a
## Breaking Changes
-Refer to the [C# SDK Versioning](https://modelcontextprotocol.github.io/csharp-sdk/versioning.html) documentation for details on versioning and breaking change policies.
+Refer to the [C# SDK Versioning](https://csharp.sdk.modelcontextprotocol.io/versioning.html) documentation for details on versioning and breaking change policies.
1. **Description #PR**
* Detail of the break
diff --git a/README.md b/README.md
index 922e867..7f5a9e1 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[](https://www.nuget.org/packages/ModelContextProtocol)
-The official C# SDK for the [Model Context Protocol](https://modelcontextprotocol.io/), enabling .NET applications, services, and libraries to implement and interact with MCP clients and servers. Please visit the [API documentation](https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.html) for more details on available functionality.
+The official C# SDK for the [Model Context Protocol](https://modelcontextprotocol.io/), enabling .NET applications, services, and libraries to implement and interact with MCP clients and servers. Please visit the [API documentation](https://csharp.sdk.modelcontextprotocol.io/api/ModelContextProtocol.html) for more details on available functionality.
## Packages
@@ -16,9 +16,9 @@ This SDK consists of three main packages:
## Getting Started
-To get started, see the [Getting Started](https://modelcontextprotocol.github.io/csharp-sdk/concepts/getting-started.html) guide in the conceptual documentation for installation instructions, package-selection guidance, and complete examples for both clients and servers.
+To get started, see the [Getting Started](https://csharp.sdk.modelcontextprotocol.io/concepts/getting-started.html) guide in the conceptual documentation for installation instructions, package-selection guidance, and complete examples for both clients and servers.
-You can also browse the [samples](samples) directory and the [API documentation](https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.html) for more details on available functionality.
+You can also browse the [samples](samples) directory and the [API documentation](https://csharp.sdk.modelcontextprotocol.io/api/ModelContextProtocol.html) for more details on available functionality.
## About MCP
@@ -27,7 +27,7 @@ The Model Context Protocol (MCP) is an open protocol that standardizes how appli
For more information about MCP:
- [Official MCP Documentation](https://modelcontextprotocol.io/)
-- [MCP C# SDK Documentation](https://modelcontextprotocol.github.io/csharp-sdk/)
+- [MCP C# SDK Documentation](https://csharp.sdk.modelcontextprotocol.io/)
- [Protocol Specification](https://modelcontextprotocol.io/specification/)
- [GitHub Organization](https://github.com/modelcontextprotocol)
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index f98deb9..c6b3f7b 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,7 +2,7 @@
- https://modelcontextprotocol.github.io/csharp-sdk
+ https://csharp.sdk.modelcontextprotocol.io
https://github.com/modelcontextprotocol/csharp-sdk
git
1.1.0
diff --git a/src/PACKAGE.md b/src/PACKAGE.md
index bc182d0..d849a8f 100644
--- a/src/PACKAGE.md
+++ b/src/PACKAGE.md
@@ -2,7 +2,7 @@
[](https://www.nuget.org/packages/ModelContextProtocol)
-The official C# SDK for the [Model Context Protocol](https://modelcontextprotocol.io/), enabling .NET applications, services, and libraries to implement and interact with MCP clients and servers. Please visit the [API documentation](https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.html) for more details on available functionality.
+The official C# SDK for the [Model Context Protocol](https://modelcontextprotocol.io/), enabling .NET applications, services, and libraries to implement and interact with MCP clients and servers. Please visit the [API documentation](https://csharp.sdk.modelcontextprotocol.io/api/ModelContextProtocol.html) for more details on available functionality.
## Packages
@@ -16,9 +16,9 @@ This SDK consists of three main packages:
## Getting Started
-To get started, see the [Getting Started](https://modelcontextprotocol.github.io/csharp-sdk/concepts/getting-started.html) guide for installation instructions, package-selection guidance, and complete examples for both clients and servers.
+To get started, see the [Getting Started](https://csharp.sdk.modelcontextprotocol.io/concepts/getting-started.html) guide for installation instructions, package-selection guidance, and complete examples for both clients and servers.
-You can also browse the [samples](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples) directory and the [API documentation](https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.html) for more details on available functionality.
+You can also browse the [samples](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples) directory and the [API documentation](https://csharp.sdk.modelcontextprotocol.io/api/ModelContextProtocol.html) for more details on available functionality.
## About MCP
@@ -27,6 +27,6 @@ The Model Context Protocol (MCP) is an open protocol that standardizes how appli
For more information about MCP:
- [Official MCP Documentation](https://modelcontextprotocol.io/)
-- [MCP C# SDK Documentation](https://modelcontextprotocol.github.io/csharp-sdk/)
+- [MCP C# SDK Documentation](https://csharp.sdk.modelcontextprotocol.io/)
- [Protocol Specification](https://modelcontextprotocol.io/specification/)
- [GitHub Organization](https://github.com/modelcontextprotocol)
From 8970b0a9e5306dca6f71710f366fc3f1e5031b5f Mon Sep 17 00:00:00 2001
From: Jeff Handley
Date: Wed, 18 Mar 2026 11:12:24 -0700
Subject: [PATCH 002/182] [release-notes skill] Harvest Co-authored-by trailers
from all commits in every PR (#1429)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.github/skills/prepare-release/SKILL.md | 6 +++---
.github/skills/prepare-release/references/categorization.md | 4 ++--
.github/skills/publish-release/SKILL.md | 4 ++--
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md
index 4843205..03976ac 100644
--- a/.github/skills/prepare-release/SKILL.md
+++ b/.github/skills/prepare-release/SKILL.md
@@ -45,8 +45,8 @@ Sort every PR into one of four categories. See [references/categorization.md](re
```
**Attribution rules:**
-- Harvest `Co-authored-by` trailers from all commits in each PR's merge commit
-- For Copilot-authored PRs, check the `copilot_work_started` timeline event to identify the triggering user. That person becomes the primary author; `@Copilot` becomes a co-author
+- Harvest `Co-authored-by` trailers from **all commits** in each PR (not just the merge commit) to identify co-authors. Do this for every PR regardless of primary author.
+- For Copilot-authored PRs, additionally check the `copilot_work_started` timeline event to identify the triggering user. That person becomes the primary author; `@Copilot` becomes a co-author.
- Omit the co-author parenthetical when there are none
- Sort entries within each section by merge date (chronological)
@@ -187,7 +187,7 @@ Only after explicit user confirmation in Step 11:
## Edge Cases
- **PR spans categories**: categorize by primary intent
-- **Copilot timeline missing**: fall back to `Co-authored-by` trailers; if still unclear, use `@Copilot` as primary author
+- **Copilot timeline missing**: fall back to `Co-authored-by` trailers to determine whether `@Copilot` should be a co-author; if still unclear, use `@Copilot` as primary author
- **No breaking changes**: omit the Breaking Changes section from release notes entirely
- **Single breaking change**: use the same numbered format as multiple
- **No user-facing changes**: if all PRs are documentation, tests, or infrastructure, flag that a release may not be warranted and ask the user whether to proceed
diff --git a/.github/skills/prepare-release/references/categorization.md b/.github/skills/prepare-release/references/categorization.md
index f9a98cc..8445667 100644
--- a/.github/skills/prepare-release/references/categorization.md
+++ b/.github/skills/prepare-release/references/categorization.md
@@ -49,7 +49,7 @@ Use this simplified format (GitHub auto-links `#PR` and `@user`):
* Description #PR by @author
```
-For PRs with co-authors (harvested from `Co-authored-by` commit trailers):
+For PRs with co-authors (harvested from `Co-authored-by` trailers across **all commits** in the PR, not just the merge commit):
```
* Description #PR by @author (co-authored by @user1 @user2)
```
@@ -64,7 +64,7 @@ For direct commits without an associated PR (e.g., version bumps merged directly
* Bump version to v0.1.0-preview.12 by @halter73
```
-For Copilot-authored PRs, identify who triggered Copilot using the `copilot_work_started` timeline event on the PR. That person becomes the primary author, and @Copilot becomes a co-author:
+For Copilot-authored PRs, additionally identify who triggered Copilot using the `copilot_work_started` timeline event on the PR. That person becomes the primary author, and @Copilot becomes a co-author:
```
* Add trace-level logging for JSON-RPC payloads #1234 by @halter73 (co-authored by @Copilot)
```
diff --git a/.github/skills/publish-release/SKILL.md b/.github/skills/publish-release/SKILL.md
index de11958..fce598c 100644
--- a/.github/skills/publish-release/SKILL.md
+++ b/.github/skills/publish-release/SKILL.md
@@ -64,7 +64,7 @@ Re-categorize all PRs in the commit range (including any new ones from Step 3).
1. **Re-run the breaking change audit** using the **breaking-changes** skill if new PRs were found that may introduce breaks. Otherwise, carry forward the results from the prepare-release PR.
2. **Re-categorize** all PRs into sections (What's Changed, Documentation, Tests, Infrastructure).
-3. **Re-attribute** co-authors for any new PRs.
+3. **Re-attribute** co-authors for any new PRs by harvesting `Co-authored-by` trailers from all commits in each PR.
4. **Update acknowledgements** to include contributors from new PRs.
### Step 5: Validate README Code Samples
@@ -125,7 +125,7 @@ When the user requests revisions after the initial creation, always rewrite the
- **PR not found**: if the prepare-release PR cannot be identified, offer to proceed manually by specifying a version and target commit
- **Draft already exists**: if a draft release with the same tag already exists, offer to update it
- **PR spans categories**: categorize by primary intent
-- **Copilot timeline missing**: fall back to `Co-authored-by` trailers; if still unclear, use `@Copilot` as primary author
+- **Copilot timeline missing**: fall back to `Co-authored-by` trailers to determine whether `@Copilot` should be a co-author; if still unclear, use `@Copilot` as primary author
- **No breaking changes**: omit the Breaking Changes section entirely
- **Single breaking change**: use the same numbered format as multiple
From 4140c6d6e405636f0486e4b85eaca2e8b547b377 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 16:16:51 -0700
Subject: [PATCH 003/182] Update System10Version to 10.0.4 and
MicrosoftExtensionsVersion to 10.4.0 (#1445)
---
Directory.Packages.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 3c2b77b..2a8fff7 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,8 +3,8 @@
true
8.0.22
9.0.11
- 10.0.3
- 10.3.0
+ 10.0.4
+ 10.4.0
From 2958a758948d46b0126c76e318ec75651ff2302c Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Sun, 22 Mar 2026 21:19:29 -0400
Subject: [PATCH 004/182] Support specifying OutputSchema independently of
return type for tools returning CallToolResult (#1425)
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 Toub
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../Server/AIFunctionMcpServerTool.cs | 19 +-
.../Server/McpServerToolAttribute.cs | 21 ++
.../Server/McpServerToolCreateOptions.cs | 21 ++
.../Server/McpServerToolTests.cs | 275 ++++++++++++++++++
4 files changed, 335 insertions(+), 1 deletion(-)
diff --git a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs
index e91bdd2..700d9d2 100644
--- a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs
+++ b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs
@@ -206,6 +206,13 @@ internal sealed partial class AIFunctionMcpServerTool : McpServerTool
newOptions.UseStructuredContent = toolAttr.UseStructuredContent;
+ if (toolAttr.OutputSchemaType is Type outputSchemaType)
+ {
+ newOptions.OutputSchema ??= AIJsonUtilities.CreateJsonSchema(outputSchemaType,
+ serializerOptions: newOptions.SerializerOptions ?? McpJsonUtilities.DefaultOptions,
+ inferenceOptions: newOptions.SchemaCreateOptions);
+ }
+
if (toolAttr._taskSupport is { } taskSupport)
{
newOptions.Execution ??= new ToolExecution();
@@ -487,7 +494,17 @@ internal sealed partial class AIFunctionMcpServerTool : McpServerTool
return null;
}
- if (function.ReturnJsonSchema is not JsonElement outputSchema)
+ // Explicit OutputSchema takes precedence over AIFunction's return schema.
+ JsonElement outputSchema;
+ if (toolCreateOptions.OutputSchema is { } explicitSchema)
+ {
+ outputSchema = explicitSchema;
+ }
+ else if (function.ReturnJsonSchema is { } returnSchema)
+ {
+ outputSchema = returnSchema;
+ }
+ else
{
return null;
}
diff --git a/src/ModelContextProtocol.Core/Server/McpServerToolAttribute.cs b/src/ModelContextProtocol.Core/Server/McpServerToolAttribute.cs
index 21a227e..d67bac1 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerToolAttribute.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerToolAttribute.cs
@@ -265,6 +265,27 @@ public sealed class McpServerToolAttribute : Attribute
///
public bool UseStructuredContent { get; set; }
+ ///
+ /// Gets or sets a from which to generate the tool's output schema.
+ ///
+ ///
+ /// The default is , which means the output schema is inferred from the return type.
+ ///
+ ///
+ ///
+ /// When set, a JSON schema is generated from the specified and used as the
+ /// instead of the schema inferred from the tool method's return type.
+ /// This is particularly useful when a tool method returns directly
+ /// (to control properties like , ,
+ /// or ) but still needs to advertise a meaningful output
+ /// schema to clients.
+ ///
+ ///
+ /// must also be set to for this property to take effect.
+ ///
+ ///
+ public Type? OutputSchemaType { get; set; }
+
///
/// Gets or sets the source URI for the tool's icon.
///
diff --git a/src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs
index 3bf0c53..88d718d 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs
@@ -129,6 +129,26 @@ public sealed class McpServerToolCreateOptions
///
public bool UseStructuredContent { get; set; }
+ ///
+ /// Gets or sets an explicit JSON schema to use as the tool's output schema.
+ ///
+ ///
+ /// The default is , which means the output schema is inferred from the return type.
+ ///
+ ///
+ ///
+ /// When set, this schema is used as the instead of the schema
+ /// inferred from the tool method's return type. This is particularly useful when a tool method
+ /// returns directly (to control properties like ,
+ /// , or ) but still
+ /// needs to advertise a meaningful output schema to clients.
+ ///
+ ///
+ /// must also be set to for this property to take effect.
+ ///
+ ///
+ public JsonElement? OutputSchema { get; set; }
+
///
/// Gets or sets the JSON serializer options to use when marshalling data to/from JSON.
///
@@ -209,6 +229,7 @@ public sealed class McpServerToolCreateOptions
OpenWorld = OpenWorld,
ReadOnly = ReadOnly,
UseStructuredContent = UseStructuredContent,
+ OutputSchema = OutputSchema,
SerializerOptions = SerializerOptions,
SchemaCreateOptions = SchemaCreateOptions,
Metadata = Metadata,
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
index fd62a05..aa20a85 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
@@ -533,6 +533,273 @@ public partial class McpServerToolTests
Assert.Null(result.StructuredContent);
}
+ [Fact]
+ public void OutputSchema_Options_OverridesReturnTypeSchema()
+ {
+ // When OutputSchema is set on options, it should be used instead of the return type's schema
+ JsonElement outputSchema = JsonDocument.Parse("""{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"}},"required":["name","age"]}""").RootElement;
+ McpServerTool tool = McpServerTool.Create(() => "result", new()
+ {
+ UseStructuredContent = true,
+ OutputSchema = outputSchema,
+ });
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.True(tool.ProtocolTool.OutputSchema.Value.TryGetProperty("properties", out var properties));
+ Assert.True(properties.TryGetProperty("name", out _));
+ Assert.True(properties.TryGetProperty("age", out _));
+ }
+
+ [Fact]
+ public void OutputSchema_Options_WithCallToolResultReturn()
+ {
+ // When the tool returns CallToolResult, OutputSchema on options provides the advertised schema
+ JsonElement outputSchema = JsonDocument.Parse("""{"type":"object","properties":{"result":{"type":"string"}},"required":["result"]}""").RootElement;
+ McpServerTool tool = McpServerTool.Create(() => new CallToolResult() { Content = [] }, new()
+ {
+ UseStructuredContent = true,
+ OutputSchema = outputSchema,
+ });
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.Equal("object", tool.ProtocolTool.OutputSchema.Value.GetProperty("type").GetString());
+ Assert.True(tool.ProtocolTool.OutputSchema.Value.TryGetProperty("properties", out var properties));
+ Assert.True(properties.TryGetProperty("result", out _));
+ }
+
+ [Fact]
+ public async Task OutputSchema_Options_CallToolResult_PreservesStructuredContent()
+ {
+ // When tool returns CallToolResult with StructuredContent, it's preserved in the response
+ JsonElement outputSchema = JsonDocument.Parse("""{"type":"object","properties":{"value":{"type":"integer"}},"required":["value"]}""").RootElement;
+ JsonElement structuredContent = JsonDocument.Parse("""{"value":42}""").RootElement;
+ McpServerTool tool = McpServerTool.Create(() => new CallToolResult()
+ {
+ Content = [new TextContentBlock { Text = "42" }],
+ StructuredContent = structuredContent,
+ }, new()
+ {
+ Name = "tool",
+ UseStructuredContent = true,
+ OutputSchema = outputSchema,
+ });
+ var mockServer = new Mock();
+ var request = new RequestContext(mockServer.Object, CreateTestJsonRpcRequest())
+ {
+ Params = new CallToolRequestParams { Name = "tool" },
+ };
+
+ var result = await tool.InvokeAsync(request, TestContext.Current.CancellationToken);
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.NotNull(result.StructuredContent);
+ Assert.Equal(42, result.StructuredContent.Value.GetProperty("value").GetInt32());
+ AssertMatchesJsonSchema(tool.ProtocolTool.OutputSchema.Value, result.StructuredContent);
+ }
+
+ [Fact]
+ public void OutputSchema_Options_RequiresUseStructuredContent()
+ {
+ // OutputSchema without UseStructuredContent=true should not produce an output schema
+ JsonElement outputSchema = JsonDocument.Parse("""{"type":"object","properties":{"name":{"type":"string"}}}""").RootElement;
+ McpServerTool tool = McpServerTool.Create(() => "result", new()
+ {
+ UseStructuredContent = false,
+ OutputSchema = outputSchema,
+ });
+
+ Assert.Null(tool.ProtocolTool.OutputSchema);
+ }
+
+ [Fact]
+ public void OutputSchema_Options_NonObjectSchema_GetsWrapped()
+ {
+ // Non-object output schema should be wrapped in a "result" property envelope
+ JsonElement outputSchema = JsonDocument.Parse("""{"type":"string"}""").RootElement;
+ McpServerTool tool = McpServerTool.Create(() => "result", new()
+ {
+ UseStructuredContent = true,
+ OutputSchema = outputSchema,
+ });
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.Equal("object", tool.ProtocolTool.OutputSchema.Value.GetProperty("type").GetString());
+ Assert.True(tool.ProtocolTool.OutputSchema.Value.TryGetProperty("properties", out var properties));
+ Assert.True(properties.TryGetProperty("result", out var resultProp));
+ Assert.Equal("string", resultProp.GetProperty("type").GetString());
+ }
+
+ [Fact]
+ public void OutputSchema_Options_NullableObjectSchema_BecomesObject()
+ {
+ // ["object", "null"] type should be simplified to just "object"
+ JsonElement outputSchema = JsonDocument.Parse("""{"type":["object","null"],"properties":{"name":{"type":"string"}}}""").RootElement;
+ McpServerTool tool = McpServerTool.Create(() => "result", new()
+ {
+ UseStructuredContent = true,
+ OutputSchema = outputSchema,
+ });
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.Equal("object", tool.ProtocolTool.OutputSchema.Value.GetProperty("type").GetString());
+ }
+
+ [Fact]
+ public void OutputSchema_Attribute_WithType_GeneratesSchema()
+ {
+ McpServerTool tool = McpServerTool.Create(ToolWithOutputSchemaAttribute, new() { SerializerOptions = CreateSerializerOptionsWithPerson() });
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.Equal("object", tool.ProtocolTool.OutputSchema.Value.GetProperty("type").GetString());
+ Assert.True(tool.ProtocolTool.OutputSchema.Value.TryGetProperty("properties", out var properties));
+ Assert.True(properties.TryGetProperty("name", out _));
+ Assert.True(properties.TryGetProperty("age", out _));
+ }
+
+ [Fact]
+ public async Task OutputSchema_Attribute_CallToolResult_PreservesStructuredContent()
+ {
+ McpServerTool tool = McpServerTool.Create(ToolWithOutputSchemaAttribute, new() { SerializerOptions = CreateSerializerOptionsWithPerson() });
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.Equal("object", tool.ProtocolTool.OutputSchema.Value.GetProperty("type").GetString());
+
+ var mockServer = new Mock();
+ var request = new RequestContext(mockServer.Object, CreateTestJsonRpcRequest())
+ {
+ Params = new CallToolRequestParams { Name = "tool" },
+ };
+
+ var result = await tool.InvokeAsync(request, TestContext.Current.CancellationToken);
+
+ Assert.NotNull(result.StructuredContent);
+ Assert.Equal("John", result.StructuredContent.Value.GetProperty("name").GetString());
+ Assert.Equal(27, result.StructuredContent.Value.GetProperty("age").GetInt32());
+ AssertMatchesJsonSchema(tool.ProtocolTool.OutputSchema.Value, result.StructuredContent);
+ }
+
+ [Fact]
+ public void OutputSchema_Attribute_WithoutUseStructuredContent_NoSchema()
+ {
+ // If UseStructuredContent is false but OutputSchema type is set, no output schema should be generated
+ McpServerTool tool = McpServerTool.Create(ToolWithOutputSchemaButNoStructuredContent, new() { SerializerOptions = CreateSerializerOptionsWithPerson() });
+
+ Assert.Null(tool.ProtocolTool.OutputSchema);
+ }
+
+ [Fact]
+ public void OutputSchema_Options_TakesPrecedenceOverAttribute()
+ {
+ // Options.OutputSchema should take precedence over attribute-derived schema
+ JsonElement outputSchema = JsonDocument.Parse("""{"type":"object","properties":{"custom":{"type":"boolean"}},"required":["custom"]}""").RootElement;
+ McpServerTool tool = McpServerTool.Create(ToolWithOutputSchemaAttribute, new()
+ {
+ OutputSchema = outputSchema,
+ });
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.True(tool.ProtocolTool.OutputSchema.Value.TryGetProperty("properties", out var properties));
+ Assert.True(properties.TryGetProperty("custom", out _));
+ // Should not have Person's properties
+ Assert.False(properties.TryGetProperty("name", out _));
+ }
+
+ [Fact]
+ public void OutputSchema_Options_Clone_PreservesValue()
+ {
+ // Verify that Clone() preserves the OutputSchema property
+ JsonElement outputSchema = JsonDocument.Parse("""{"type":"object","properties":{"x":{"type":"integer"}}}""").RootElement;
+ McpServerTool tool1 = McpServerTool.Create(() => "result", new()
+ {
+ Name = "tool1",
+ UseStructuredContent = true,
+ OutputSchema = outputSchema,
+ });
+
+ // The output schema should be present since we set it
+ Assert.NotNull(tool1.ProtocolTool.OutputSchema);
+ Assert.True(tool1.ProtocolTool.OutputSchema.Value.TryGetProperty("properties", out var props));
+ Assert.True(props.TryGetProperty("x", out _));
+ }
+
+ [Fact]
+ public async Task OutputSchema_Options_PersonType_WithCallToolResult()
+ {
+ // Create output schema from Person type, tool returns CallToolResult with matching structured content
+ JsonSerializerOptions serializerOptions = new() { TypeInfoResolver = new DefaultJsonTypeInfoResolver() };
+ JsonElement outputSchema = AIJsonUtilities.CreateJsonSchema(typeof(Person), serializerOptions: serializerOptions);
+ Person person = new("Alice", 30);
+ JsonElement structuredContent = JsonSerializer.SerializeToElement(person, serializerOptions);
+ McpServerTool tool = McpServerTool.Create(() => new CallToolResult()
+ {
+ Content = [new TextContentBlock { Text = "Alice, 30" }],
+ StructuredContent = structuredContent,
+ }, new()
+ {
+ Name = "tool",
+ UseStructuredContent = true,
+ OutputSchema = outputSchema,
+ SerializerOptions = serializerOptions,
+ });
+ var mockServer = new Mock();
+ var request = new RequestContext(mockServer.Object, CreateTestJsonRpcRequest())
+ {
+ Params = new CallToolRequestParams { Name = "tool" },
+ };
+
+ var result = await tool.InvokeAsync(request, TestContext.Current.CancellationToken);
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.NotNull(result.StructuredContent);
+ AssertMatchesJsonSchema(tool.ProtocolTool.OutputSchema.Value, result.StructuredContent);
+ }
+
+ [Fact]
+ public async Task OutputSchema_Options_OverridesReturnTypeSchema_InvokeAndValidate()
+ {
+ // OutputSchema overrides return type schema; result should match the original return type, but schema is the override
+ JsonSerializerOptions serializerOptions = new() { TypeInfoResolver = new DefaultJsonTypeInfoResolver() };
+ JsonElement outputSchema = AIJsonUtilities.CreateJsonSchema(typeof(Person), serializerOptions: serializerOptions);
+ McpServerTool tool = McpServerTool.Create(() => new Person("Bob", 25), new()
+ {
+ Name = "tool",
+ UseStructuredContent = true,
+ OutputSchema = outputSchema,
+ SerializerOptions = serializerOptions,
+ });
+ var mockServer = new Mock();
+ var request = new RequestContext(mockServer.Object, CreateTestJsonRpcRequest())
+ {
+ Params = new CallToolRequestParams { Name = "tool" },
+ };
+
+ var result = await tool.InvokeAsync(request, TestContext.Current.CancellationToken);
+
+ Assert.NotNull(tool.ProtocolTool.OutputSchema);
+ Assert.NotNull(result.StructuredContent);
+ AssertMatchesJsonSchema(tool.ProtocolTool.OutputSchema.Value, result.StructuredContent);
+ }
+
+ [McpServerTool(UseStructuredContent = true, OutputSchemaType = typeof(Person))]
+ private static CallToolResult ToolWithOutputSchemaAttribute()
+ {
+ var person = new Person("John", 27);
+ return new CallToolResult()
+ {
+ Content = [new TextContentBlock { Text = $"{person.Name}, {person.Age}" }],
+ StructuredContent = JsonSerializer.SerializeToElement(person, JsonContext2.Default.Person),
+ };
+ }
+
+ [McpServerTool(UseStructuredContent = false, OutputSchemaType = typeof(Person))]
+ private static CallToolResult ToolWithOutputSchemaButNoStructuredContent()
+ {
+ return new CallToolResult()
+ {
+ Content = [new TextContentBlock { Text = "result" }],
+ };
+ }
+
[Theory]
[InlineData(JsonNumberHandling.Strict)]
[InlineData(JsonNumberHandling.AllowReadingFromString)]
@@ -679,6 +946,13 @@ public partial class McpServerToolTests
record Person(string Name, int Age);
+ private static JsonSerializerOptions CreateSerializerOptionsWithPerson()
+ {
+ JsonSerializerOptions options = new(McpJsonUtilities.DefaultOptions);
+ options.TypeInfoResolverChain.Add(JsonContext2.Default);
+ return options;
+ }
+
[Fact]
public void SupportsIconsInCreateOptions()
{
@@ -928,5 +1202,6 @@ public partial class McpServerToolTests
[JsonSerializable(typeof(List))]
[JsonSerializable(typeof(int?))]
[JsonSerializable(typeof(DateTimeOffset?))]
+ [JsonSerializable(typeof(Person))]
partial class JsonContext2 : JsonSerializerContext;
}
From af2d40ba146ca5dec2cd383f9b8b2cd74843b229 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 22 Mar 2026 21:20:12 -0400
Subject: [PATCH 005/182] Bump the other-testing group with 2 updates (#1440)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
Directory.Packages.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2a8fff7..937b2a3 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,7 +3,7 @@
true
8.0.22
9.0.11
- 10.0.4
+ 10.0.5
10.4.0
@@ -92,6 +92,6 @@
-
+
\ No newline at end of file
From 57abd203d9279919bfa104753e00762f96744c81 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 23 Mar 2026 01:37:15 -0700
Subject: [PATCH 006/182] Bump danielpalme/ReportGenerator-GitHub-Action from
5.5.2 to 5.5.4 (#1438)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Stephen Toub
---
.github/workflows/ci-code-coverage.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci-code-coverage.yml b/.github/workflows/ci-code-coverage.yml
index 88b31de..c4d4a05 100644
--- a/.github/workflows/ci-code-coverage.yml
+++ b/.github/workflows/ci-code-coverage.yml
@@ -24,7 +24,7 @@ jobs:
pattern: testresults-*
- name: Combine coverage reports
- uses: danielpalme/ReportGenerator-GitHub-Action@5.5.2
+ uses: danielpalme/ReportGenerator-GitHub-Action@5.5.4
with:
reports: "**/*.cobertura.xml"
targetdir: "${{ github.workspace }}/report"
From 83aee9cbe26c2e9c5a4f859f7b657122bfda8842 Mon Sep 17 00:00:00 2001
From: Stephen Toub
Date: Mon, 23 Mar 2026 07:17:51 -0400
Subject: [PATCH 007/182] Update Microsoft.Extensions.AI.OpenAI version
reference (#1456)
---
Directory.Packages.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 937b2a3..2f594a2 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -67,7 +67,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
@@ -94,4 +94,4 @@
-
\ No newline at end of file
+
From fd7f2c0eb8937131cda8da252133ba53f73ffe8f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 23 Mar 2026 13:21:28 +0200
Subject: [PATCH 008/182] Bump Microsoft.Extensions.AI from 10.4.0 to 10.4.1
(#1461)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2f594a2..c5bbfe5 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -4,7 +4,7 @@
8.0.22
9.0.11
10.0.5
- 10.4.0
+ 10.4.1
From 51a4fde4d9cfa12ef9430deef7daeaac36625be8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 23 Mar 2026 13:21:34 +0200
Subject: [PATCH 009/182] Bump Anthropic from 12.8.0 to 12.9.0 (#1460)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c5bbfe5..c45dbaa 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -62,7 +62,7 @@
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
From e700951bd819f8f0ee8459e767c88370420c0330 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Fri, 27 Mar 2026 07:25:45 -0400
Subject: [PATCH 010/182] docs: align roots terminology with MCP spec
clarification (#1469)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
docs/concepts/roots/roots.md | 2 +-
.../Protocol/NotificationMethods.cs | 7 ++++---
src/ModelContextProtocol.Core/Protocol/Root.cs | 7 ++++---
src/ModelContextProtocol.Core/Protocol/RootsCapability.cs | 6 +++---
4 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/docs/concepts/roots/roots.md b/docs/concepts/roots/roots.md
index 94b3308..8205cb6 100644
--- a/docs/concepts/roots/roots.md
+++ b/docs/concepts/roots/roots.md
@@ -17,7 +17,7 @@ Roots provide a mechanism for the client to tell the server which directories, p
- Scope file searches to the user's project directories
- Understand which repositories are being worked on
-- Limit operations to specific filesystem boundaries
+- Focus operations on relevant filesystem locations
Each root is represented by a with a URI and an optional human-readable name.
diff --git a/src/ModelContextProtocol.Core/Protocol/NotificationMethods.cs b/src/ModelContextProtocol.Core/Protocol/NotificationMethods.cs
index 7fb15a0..9493616 100644
--- a/src/ModelContextProtocol.Core/Protocol/NotificationMethods.cs
+++ b/src/ModelContextProtocol.Core/Protocol/NotificationMethods.cs
@@ -53,9 +53,10 @@ public static class NotificationMethods
///
///
/// This notification informs the server that the client's "roots" have changed.
- /// Roots define the boundaries of where servers can operate within the filesystem,
- /// allowing them to understand which directories and files they have access to. Servers
- /// can request the list of roots from supporting clients and receive notifications when that list changes.
+ /// Roots inform servers about the directories and files the client considers relevant,
+ /// so that servers can focus their operations accordingly. They are informational guidance
+ /// rather than an access-control mechanism; the protocol does not enforce that servers stay within roots.
+ /// Servers can request the list of roots from supporting clients and receive notifications when that list changes.
///
///
/// After receiving this notification, servers can refresh their knowledge of roots by calling the appropriate
diff --git a/src/ModelContextProtocol.Core/Protocol/Root.cs b/src/ModelContextProtocol.Core/Protocol/Root.cs
index 0a86aaf..622dbdd 100644
--- a/src/ModelContextProtocol.Core/Protocol/Root.cs
+++ b/src/ModelContextProtocol.Core/Protocol/Root.cs
@@ -9,9 +9,10 @@ namespace ModelContextProtocol.Protocol;
///
///
/// Root URIs serve as entry points for resource navigation, typically representing
-/// top-level directories or container resources that can be accessed and traversed.
-/// Roots provide a hierarchical structure for organizing and accessing resources within the protocol.
-/// Each root has a URI that uniquely identifies it and optional metadata like a human-readable name.
+/// top-level directories or container resources that are relevant to the current session.
+/// Roots inform servers which locations the client considers important, providing informational
+/// guidance rather than an access-control mechanism. Each root has a URI that uniquely identifies
+/// it and optional metadata like a human-readable name.
///
public sealed class Root
{
diff --git a/src/ModelContextProtocol.Core/Protocol/RootsCapability.cs b/src/ModelContextProtocol.Core/Protocol/RootsCapability.cs
index 4739cde..0b2f9e7 100644
--- a/src/ModelContextProtocol.Core/Protocol/RootsCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/RootsCapability.cs
@@ -13,9 +13,9 @@ namespace ModelContextProtocol.Protocol;
/// root URIs that serve as entry points for resource navigation.
///
///
-/// The roots capability establishes a mechanism for servers to discover and access the hierarchical
-/// structure of resources provided by a client. Root URIs represent top-level entry points from which
-/// servers can navigate to access specific resources.
+/// The roots capability establishes a mechanism for servers to discover the directories and files
+/// the client considers relevant. Root URIs represent top-level entry points that inform the server
+/// about the working context, providing informational guidance rather than enforcing access control.
///
///
/// See the schema for details.
From afc40d1e2eeef5be43fec4101a794bdf504fdd7e Mon Sep 17 00:00:00 2001
From: Stephen Halter
Date: Fri, 27 Mar 2026 09:09:45 -0700
Subject: [PATCH 011/182] Route SendRequestAsync logic through outgoing message
filters (#1465)
---
.../McpSessionHandler.cs | 69 ++++---
.../MapMcpTests.cs | 195 ++++++++++++++++++
...rverBuilderExtensionsMessageFilterTests.cs | 169 ++++++++++++++-
3 files changed, 393 insertions(+), 40 deletions(-)
diff --git a/src/ModelContextProtocol.Core/McpSessionHandler.cs b/src/ModelContextProtocol.Core/McpSessionHandler.cs
index 6c47573..048ae04 100644
--- a/src/ModelContextProtocol.Core/McpSessionHandler.cs
+++ b/src/ModelContextProtocol.Core/McpSessionHandler.cs
@@ -576,15 +576,6 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
AddTags(ref tags, activity, request, method, target);
}
- if (_logger.IsEnabled(LogLevel.Trace))
- {
- LogSendingRequestSensitive(EndpointName, request.Method, JsonSerializer.Serialize(request, McpJsonUtilities.JsonContext.Default.JsonRpcMessage));
- }
- else
- {
- LogSendingRequest(EndpointName, request.Method);
- }
-
await SendToRelatedTransportAsync(request, cancellationToken).ConfigureAwait(false);
// Now that the request has been sent, register for cancellation. If we registered before,
@@ -671,29 +662,17 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
AddTags(ref tags, activity, message, method, target);
}
- await _outgoingMessageFilter(async (msg, ct) =>
+ await SendToRelatedTransportAsync(message, cancellationToken).ConfigureAwait(false);
+
+ // If the sent notification was a cancellation notification, cancel the pending request's await, as either the
+ // server won't be sending a response, or per the specification, the response should be ignored. There are inherent
+ // race conditions here, so it's possible and allowed for the operation to complete before we get to this point.
+ if (message is JsonRpcNotification { Method: NotificationMethods.CancelledNotification } notification &&
+ GetCancelledNotificationParams(notification.Params) is CancelledNotificationParams cn &&
+ _pendingRequests.TryRemove(cn.RequestId, out var tcs))
{
- if (_logger.IsEnabled(LogLevel.Trace))
- {
- LogSendingMessageSensitive(EndpointName, JsonSerializer.Serialize(msg, McpJsonUtilities.JsonContext.Default.JsonRpcMessage));
- }
- else
- {
- LogSendingMessage(EndpointName);
- }
-
- await SendToRelatedTransportAsync(msg, ct).ConfigureAwait(false);
-
- // If the sent notification was a cancellation notification, cancel the pending request's await, as either the
- // server won't be sending a response, or per the specification, the response should be ignored. There are inherent
- // race conditions here, so it's possible and allowed for the operation to complete before we get to this point.
- if (msg is JsonRpcNotification { Method: NotificationMethods.CancelledNotification } notification &&
- GetCancelledNotificationParams(notification.Params) is CancelledNotificationParams cn &&
- _pendingRequests.TryRemove(cn.RequestId, out var tcs))
- {
- tcs.TrySetCanceled(default);
- }
- })(message, cancellationToken).ConfigureAwait(false);
+ tcs.TrySetCanceled(default);
+ }
}
catch (Exception ex) when (addTags)
{
@@ -710,7 +689,33 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
// Streamable HTTP transport where the specification states that the server SHOULD include JSON-RPC responses in
// the HTTP response body for the POST request containing the corresponding JSON-RPC request.
private Task SendToRelatedTransportAsync(JsonRpcMessage message, CancellationToken cancellationToken)
- => (message.Context?.RelatedTransport ?? _transport).SendMessageAsync(message, cancellationToken);
+ => _outgoingMessageFilter((msg, ct) =>
+ {
+ if (msg is JsonRpcRequest request)
+ {
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ LogSendingRequestSensitive(EndpointName, request.Method, JsonSerializer.Serialize(msg, McpJsonUtilities.JsonContext.Default.JsonRpcMessage));
+ }
+ else
+ {
+ LogSendingRequest(EndpointName, request.Method);
+ }
+ }
+ else
+ {
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ LogSendingMessageSensitive(EndpointName, JsonSerializer.Serialize(msg, McpJsonUtilities.JsonContext.Default.JsonRpcMessage));
+ }
+ else
+ {
+ LogSendingMessage(EndpointName);
+ }
+ }
+
+ return (msg.Context?.RelatedTransport ?? _transport).SendMessageAsync(msg, ct);
+ })(message, cancellationToken);
private static CancelledNotificationParams? GetCancelledNotificationParams(JsonNode? notificationParams)
{
diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs
index bee17be..30a7a45 100644
--- a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs
+++ b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs
@@ -9,6 +9,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Security.Claims;
+using System.Text.Json.Nodes;
namespace ModelContextProtocol.AspNetCore.Tests;
@@ -290,6 +291,200 @@ public abstract class MapMcpTests(ITestOutputHelper testOutputHelper) : KestrelI
}
+ [Fact]
+ public async Task IncomingFilter_SeesClientRequests()
+ {
+ var observedMethods = new List();
+
+ Builder.Services.AddMcpServer()
+ .WithHttpTransport(ConfigureStateless)
+ .WithMessageFilters(filters => filters.AddIncomingFilter((next) => async (context, cancellationToken) =>
+ {
+ if (context.JsonRpcMessage is JsonRpcRequest request)
+ {
+ observedMethods.Add(request.Method);
+ }
+
+ await next(context, cancellationToken);
+ }))
+ .WithTools();
+
+ Builder.Services.AddHttpContextAccessor();
+
+ await using var app = Builder.Build();
+ app.MapMcp();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ await using var client = await ConnectAsync();
+
+ await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
+ await client.CallToolAsync("echo_with_user_name",
+ new Dictionary { ["message"] = "hi" },
+ cancellationToken: TestContext.Current.CancellationToken);
+
+ Assert.Contains(RequestMethods.Initialize, observedMethods);
+ Assert.Contains(RequestMethods.ToolsList, observedMethods);
+ Assert.Contains(RequestMethods.ToolsCall, observedMethods);
+ }
+
+ [Fact]
+ public async Task OutgoingFilter_SeesResponsesAndRequests()
+ {
+ Assert.SkipWhen(Stateless, "Server-originated requests are not supported in stateless mode.");
+
+ var observedMessageTypes = new List();
+
+ Builder.Services.AddMcpServer()
+ .WithHttpTransport(ConfigureStateless)
+ .WithMessageFilters(filters => filters.AddOutgoingFilter((next) => async (context, cancellationToken) =>
+ {
+ var typeName = context.JsonRpcMessage switch
+ {
+ JsonRpcRequest request => $"request:{request.Method}",
+ JsonRpcResponse r when r.Result is JsonObject obj && obj.ContainsKey("protocolVersion") => "initialize-response",
+ JsonRpcResponse r when r.Result is JsonObject obj && obj.ContainsKey("tools") => "tools-list-response",
+ JsonRpcResponse r when r.Result is JsonObject obj && obj.ContainsKey("content") => "tool-call-response",
+ _ => null,
+ };
+
+ if (typeName is not null)
+ {
+ observedMessageTypes.Add(typeName);
+ }
+
+ await next(context, cancellationToken);
+ }))
+ .WithTools()
+ .WithTools();
+
+ await using var app = Builder.Build();
+ app.MapMcp();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ var clientOptions = new McpClientOptions
+ {
+ Capabilities = new() { Sampling = new() },
+ Handlers = new()
+ {
+ SamplingHandler = (_, _, _) => new(new CreateMessageResult
+ {
+ Content = [new TextContentBlock { Text = "sampled response" }],
+ Model = "test-model",
+ }),
+ },
+ };
+
+ await using var client = await ConnectAsync(clientOptions: clientOptions);
+
+ await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
+ await client.CallToolAsync("echo_claims_principal",
+ new Dictionary { ["message"] = "hi" },
+ cancellationToken: TestContext.Current.CancellationToken);
+ await client.CallToolAsync("sampling-tool",
+ new Dictionary { ["prompt"] = "Hello" },
+ cancellationToken: TestContext.Current.CancellationToken);
+
+ Assert.Contains("initialize-response", observedMessageTypes);
+ Assert.Contains("tools-list-response", observedMessageTypes);
+ Assert.Contains("tool-call-response", observedMessageTypes);
+ Assert.Contains($"request:{RequestMethods.SamplingCreateMessage}", observedMessageTypes);
+ }
+
+ [Fact]
+ public async Task OutgoingFilter_MultipleFilters_ExecuteInOrder()
+ {
+ var executionOrder = new List();
+
+ Builder.Services.AddMcpServer()
+ .WithHttpTransport(ConfigureStateless)
+ .WithMessageFilters(filters =>
+ {
+ filters.AddOutgoingFilter((next) => async (context, cancellationToken) =>
+ {
+ if (context.JsonRpcMessage is JsonRpcResponse r && r.Result is JsonObject obj && obj.ContainsKey("tools"))
+ {
+ executionOrder.Add("filter1-before");
+ }
+
+ await next(context, cancellationToken);
+
+ if (context.JsonRpcMessage is JsonRpcResponse r2 && r2.Result is JsonObject obj2 && obj2.ContainsKey("tools"))
+ {
+ executionOrder.Add("filter1-after");
+ }
+ });
+
+ filters.AddOutgoingFilter((next) => async (context, cancellationToken) =>
+ {
+ if (context.JsonRpcMessage is JsonRpcResponse r && r.Result is JsonObject obj && obj.ContainsKey("tools"))
+ {
+ executionOrder.Add("filter2-before");
+ }
+
+ await next(context, cancellationToken);
+
+ if (context.JsonRpcMessage is JsonRpcResponse r2 && r2.Result is JsonObject obj2 && obj2.ContainsKey("tools"))
+ {
+ executionOrder.Add("filter2-after");
+ }
+ });
+ })
+ .WithTools();
+
+ await using var app = Builder.Build();
+ app.MapMcp();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ await using var client = await ConnectAsync();
+
+ await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
+
+ Assert.Equal(["filter1-before", "filter2-before", "filter2-after", "filter1-after"], executionOrder);
+ }
+
+ [Fact]
+ public async Task OutgoingFilter_CanSendAdditionalMessages()
+ {
+ Builder.Services.AddMcpServer()
+ .WithHttpTransport(ConfigureStateless)
+ .WithMessageFilters(filters => filters.AddOutgoingFilter((next) => async (context, cancellationToken) =>
+ {
+ if (context.JsonRpcMessage is JsonRpcResponse response &&
+ response.Result is JsonObject result && result.ContainsKey("tools"))
+ {
+ var extraNotification = new JsonRpcNotification
+ {
+ Method = "test/extra",
+ Params = new JsonObject { ["message"] = "injected" },
+ Context = new JsonRpcMessageContext { RelatedTransport = context.JsonRpcMessage.Context?.RelatedTransport },
+ };
+
+ await next(new MessageContext(context.Server, extraNotification), cancellationToken);
+ }
+
+ await next(context, cancellationToken);
+ }))
+ .WithTools();
+
+ await using var app = Builder.Build();
+ app.MapMcp();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ await using var client = await ConnectAsync();
+
+ var extraReceived = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ await using var registration = client.RegisterNotificationHandler("test/extra", (notification, _) =>
+ {
+ extraReceived.TrySetResult(notification.Params?["message"]?.GetValue());
+ return default;
+ });
+
+ await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
+
+ var extraMessage = await extraReceived.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
+ Assert.Equal("injected", extraMessage);
+ }
+
private ClaimsPrincipal CreateUser(string name)
=> new(new ClaimsIdentity(
[new Claim("name", name), new Claim(ClaimTypes.NameIdentifier, name)],
diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs
index ec396c5..13744ff 100644
--- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs
@@ -317,7 +317,7 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
}
[Fact]
- public async Task AddOutgoingMessageFilter_Sees_Initialize_Progress_And_Response()
+ public async Task AddOutgoingMessageFilter_Sees_Responses_Notifications_And_Requests()
{
var observedMessages = new List();
@@ -326,6 +326,9 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
{
switch (context.JsonRpcMessage)
{
+ case JsonRpcRequest request:
+ observedMessages.Add($"request:{request.Method}");
+ break;
case JsonRpcResponse response when response.Result is JsonObject result:
if (result.ContainsKey("protocolVersion"))
{
@@ -343,22 +346,41 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
await next(context, cancellationToken);
}))
- .WithTools();
+ .WithTools()
+ .WithTools();
StartServer();
- await using McpClient client = await CreateMcpClientForServer();
+ var clientOptions = new McpClientOptions
+ {
+ Capabilities = new() { Sampling = new() },
+ Handlers = new()
+ {
+ SamplingHandler = (_, _, _) => new(new CreateMessageResult
+ {
+ Content = [new TextContentBlock { Text = "sampled" }],
+ Model = "test-model",
+ }),
+ },
+ };
+
+ await using McpClient client = await CreateMcpClientForServer(clientOptions);
IProgress progress = new Progress(_ => { });
await client.CallToolAsync("progress-tool", progress: progress, cancellationToken: TestContext.Current.CancellationToken);
+ await client.CallToolAsync("sampling-tool", new Dictionary { ["prompt"] = "Hello" },
+ cancellationToken: TestContext.Current.CancellationToken);
+
int initializeIndex = observedMessages.IndexOf("initialize");
int progressIndex = observedMessages.IndexOf("progress");
int responseIndex = observedMessages.LastIndexOf("response");
+ int requestIndex = observedMessages.IndexOf($"request:{RequestMethods.SamplingCreateMessage}");
Assert.True(initializeIndex >= 0);
Assert.True(progressIndex > initializeIndex);
Assert.True(responseIndex > progressIndex);
+ Assert.True(requestIndex >= 0);
}
[Fact]
@@ -428,6 +450,127 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
Assert.Equal("extra", extraMessage);
}
+ [Fact]
+ public async Task AddOutgoingMessageFilter_SkipNext_DoesNotLogSending()
+ {
+ ServiceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Debug));
+
+ McpServerBuilder
+ .WithMessageFilters(filters => filters.AddOutgoingFilter((next) => (context, cancellationToken) =>
+ {
+ // Skip sending tool list responses
+ if (context.JsonRpcMessage is JsonRpcResponse response && response.Result is JsonObject result && result.ContainsKey("tools"))
+ {
+ return Task.CompletedTask;
+ }
+
+ return next(context, cancellationToken);
+ }))
+ .WithTools();
+
+ StartServer();
+
+ await using McpClient client = await CreateMcpClientForServer();
+
+ // Clear any logs from initialization
+ while (MockLoggerProvider.LogMessages.TryDequeue(out _)) { }
+
+ using var requestCts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
+ await Assert.ThrowsAnyAsync(async () =>
+ {
+ await client.ListToolsAsync(cancellationToken: requestCts.Token);
+ });
+
+ // Since the filter skipped next, no "sending message" log should appear for the skipped response
+ Assert.DoesNotContain(MockLoggerProvider.LogMessages, m =>
+ m.Category.Contains("McpServer") && m.Message.Contains("sending message", StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public async Task AddOutgoingMessageFilter_CallsNext_LogsSending()
+ {
+ ServiceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Debug));
+
+ McpServerBuilder
+ .WithTools();
+
+ StartServer();
+
+ await using McpClient client = await CreateMcpClientForServer();
+
+ // Clear any logs from initialization
+ while (MockLoggerProvider.LogMessages.TryDequeue(out _)) { }
+
+ await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
+
+ // The response should have been sent, producing a "sending message" log from the server
+ Assert.Contains(MockLoggerProvider.LogMessages, m =>
+ m.Category.Contains("McpServer") && m.Message.Contains("sending message", StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public async Task AddIncomingMessageFilter_SkipNext_DoesNotLogSendingResponse()
+ {
+ ServiceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Debug));
+
+ McpServerBuilder
+ .WithMessageFilters(filters => filters.AddIncomingFilter((next) => (context, cancellationToken) =>
+ {
+ // Skip processing tools/list requests — handler never runs, no response sent
+ if (context.JsonRpcMessage is JsonRpcRequest request && request.Method == RequestMethods.ToolsList)
+ {
+ return Task.CompletedTask;
+ }
+
+ return next(context, cancellationToken);
+ }))
+ .WithTools();
+
+ StartServer();
+
+ await using McpClient client = await CreateMcpClientForServer();
+
+ // Clear any logs from initialization
+ while (MockLoggerProvider.LogMessages.TryDequeue(out _)) { }
+
+ using var requestCts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
+ await Assert.ThrowsAnyAsync(async () =>
+ {
+ await client.ListToolsAsync(cancellationToken: requestCts.Token);
+ });
+
+ // Since the incoming filter skipped next, no handler ran, so no response was sent
+ Assert.DoesNotContain(MockLoggerProvider.LogMessages, m =>
+ m.Category.Contains("McpServer") && m.Message.Contains("sending message", StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public async Task AddIncomingMessageFilter_CallsNext_LogsSendingResponse()
+ {
+ ServiceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Debug));
+
+ McpServerBuilder
+ .WithMessageFilters(filters => filters.AddIncomingFilter((next) => (context, cancellationToken) =>
+ {
+ // Pass through — handler runs, response is sent
+ return next(context, cancellationToken);
+ }))
+ .WithTools();
+
+ StartServer();
+
+ await using McpClient client = await CreateMcpClientForServer();
+
+ // Clear any logs from initialization
+ while (MockLoggerProvider.LogMessages.TryDequeue(out _)) { }
+
+ await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
+
+ // The handler ran and sent a response, producing a "sending message" log from the server
+ Assert.Contains(MockLoggerProvider.LogMessages, m =>
+ m.Category.Contains("McpServer") && m.Message.Contains("sending message", StringComparison.OrdinalIgnoreCase));
+ }
+
[Fact]
public async Task AddIncomingMessageFilter_Items_Flow_To_Request_Filters()
{
@@ -644,7 +787,6 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
Assert.Equal("modifiedByFilter1", observedValues[1]);
}
- [McpServerToolType]
public sealed class TestTool
{
[McpServerTool]
@@ -654,7 +796,6 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
}
}
- [McpServerPromptType]
public sealed class TestPrompt
{
[McpServerPrompt]
@@ -668,7 +809,6 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
}
}
- [McpServerResourceType]
public sealed class TestResource
{
[McpServerResource(UriTemplate = "test://resource/{id}")]
@@ -678,7 +818,6 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
}
}
- [McpServerToolType]
public sealed class ProgressTool
{
[McpServerTool(Name = "progress-tool")]
@@ -708,7 +847,6 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
}
}
- [McpServerToolType]
public sealed class SimpleTool
{
[McpServerTool(Name = "simple-tool")]
@@ -717,4 +855,19 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
return "success";
}
}
+
+ public sealed class SamplingTool
+ {
+ [McpServerTool(Name = "sampling-tool")]
+ public static async Task SampleAsync(McpServer server, string prompt, CancellationToken cancellationToken)
+ {
+ var result = await server.SampleAsync(new CreateMessageRequestParams
+ {
+ Messages = [new SamplingMessage { Role = Role.User, Content = [new TextContentBlock { Text = prompt }] }],
+ MaxTokens = 100,
+ }, cancellationToken);
+
+ return $"Sampled: {Assert.IsType(Assert.Single(result.Content)).Text}";
+ }
+ }
}
From 7dcdbafe9d0f056db311d8f1412becce1f3d8db3 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Fri, 27 Mar 2026 12:33:23 -0700
Subject: [PATCH 012/182] Add 3-arg RequestContext constructor and obsolete
2-arg to eliminate null-forgiving operator usage (#1462)
---
docs/concepts/filters.md | 4 +-
.../samples/server/Tools/LoggingTools.cs | 2 +-
docs/concepts/pagination/pagination.md | 2 +-
.../samples/server/Tools/LongRunningTools.cs | 2 +-
docs/concepts/resources/resources.md | 4 +-
docs/list-of-diagnostics.md | 1 +
samples/EverythingServer/Program.cs | 9 +--
.../Resources/SimpleResourceType.cs | 2 +-
.../EverythingServer/Tools/LongRunningTool.cs | 2 +-
src/Common/Obsoletions.cs | 4 +
.../RequestHandlers.cs | 4 +-
.../Server/AIFunctionMcpServerResource.cs | 12 +--
.../Server/McpServerImpl.cs | 11 ++-
.../Server/RequestContext.cs | 17 +++-
.../Program.cs | 11 +--
.../Tools/ConformanceTools.cs | 2 +-
.../Program.cs | 49 ++++++------
.../Program.cs | 2 +-
.../Client/McpClientMetaTests.cs | 6 +-
.../Client/McpClientPromptTests.cs | 2 +-
...pClientResourceTemplateConstructorTests.cs | 2 +-
.../Client/McpClientResourceTemplateTests.cs | 2 +-
.../Client/McpClientResourceTests.cs | 2 +-
.../Client/McpClientToolTests.cs | 2 +-
...rverBuilderExtensionsMessageFilterTests.cs | 2 +-
.../McpServerBuilderExtensionsPromptsTests.cs | 19 ++---
...cpServerBuilderExtensionsResourcesTests.cs | 19 ++---
.../McpServerBuilderExtensionsToolsTests.cs | 8 +-
.../McpProtocolExceptionDataTests.cs | 2 +-
.../Protocol/ElicitationTypedTests.cs | 14 ++--
.../Server/McpServerPromptTests.cs | 32 ++++----
.../Server/McpServerResourceTests.cs | 78 +++++++++---------
.../Server/McpServerToolTests.cs | 79 ++++++-------------
33 files changed, 190 insertions(+), 219 deletions(-)
diff --git a/docs/concepts/filters.md b/docs/concepts/filters.md
index 9f63dd9..32f5c67 100644
--- a/docs/concepts/filters.md
+++ b/docs/concepts/filters.md
@@ -317,7 +317,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt
{
var logger = context.Services?.GetService>();
- logger?.LogInformation($"Processing request from {context.Params?.ProgressToken}");
+ logger?.LogInformation($"Processing request from {context.Params.ProgressToken}");
var result = await next(context, cancellationToken);
logger?.LogInformation($"Returning {result.Tools?.Count ?? 0} tools");
return result;
@@ -339,7 +339,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt
catch (Exception ex)
{
var logger = context.Services?.GetService>();
- logger?.LogError(ex, "Error while processing CallTool request for {ProgressToken}", context.Params?.ProgressToken);
+ logger?.LogError(ex, "Error while processing CallTool request for {ProgressToken}", context.Params.ProgressToken);
return new CallToolResult
{
diff --git a/docs/concepts/logging/samples/server/Tools/LoggingTools.cs b/docs/concepts/logging/samples/server/Tools/LoggingTools.cs
index 33fa3c0..8ddb9e4 100644
--- a/docs/concepts/logging/samples/server/Tools/LoggingTools.cs
+++ b/docs/concepts/logging/samples/server/Tools/LoggingTools.cs
@@ -13,7 +13,7 @@ public class LoggingTools
int duration = 10,
int steps = 10)
{
- var progressToken = context.Params?.ProgressToken;
+ var progressToken = context.Params.ProgressToken;
var stepDuration = duration / steps;
//
diff --git a/docs/concepts/pagination/pagination.md b/docs/concepts/pagination/pagination.md
index 1249acb..f019fd1 100644
--- a/docs/concepts/pagination/pagination.md
+++ b/docs/concepts/pagination/pagination.md
@@ -77,7 +77,7 @@ builder.Services.AddMcpServer()
int startIndex = 0;
// Parse cursor to determine starting position
- if (ctx.Params?.Cursor is { } cursor)
+ if (ctx.Params.Cursor is { } cursor)
{
startIndex = int.Parse(cursor);
}
diff --git a/docs/concepts/progress/samples/server/Tools/LongRunningTools.cs b/docs/concepts/progress/samples/server/Tools/LongRunningTools.cs
index 7fcd124..e02889a 100644
--- a/docs/concepts/progress/samples/server/Tools/LongRunningTools.cs
+++ b/docs/concepts/progress/samples/server/Tools/LongRunningTools.cs
@@ -15,7 +15,7 @@ public class LongRunningTools
int duration = 10,
int steps = 5)
{
- var progressToken = context.Params?.ProgressToken;
+ var progressToken = context.Params.ProgressToken;
var stepDuration = duration / steps;
for (int i = 1; i <= steps; i++)
diff --git a/docs/concepts/resources/resources.md b/docs/concepts/resources/resources.md
index c7e713c..5598d96 100644
--- a/docs/concepts/resources/resources.md
+++ b/docs/concepts/resources/resources.md
@@ -212,7 +212,7 @@ builder.Services.AddMcpServer()
.WithResources()
.WithSubscribeToResourcesHandler(async (ctx, ct) =>
{
- if (ctx.Params?.Uri is { } uri)
+ if (ctx.Params.Uri is { } uri)
{
// Track the subscription (e.g., in a concurrent dictionary)
subscriptions[ctx.Server.SessionId].TryAdd(uri, 0);
@@ -221,7 +221,7 @@ builder.Services.AddMcpServer()
})
.WithUnsubscribeFromResourcesHandler(async (ctx, ct) =>
{
- if (ctx.Params?.Uri is { } uri)
+ if (ctx.Params.Uri is { } uri)
{
subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
}
diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md
index 59666d5..16f17f7 100644
--- a/docs/list-of-diagnostics.md
+++ b/docs/list-of-diagnostics.md
@@ -36,3 +36,4 @@ When APIs are marked as obsolete, a diagnostic is emitted to warn users that the
| :------------ | :----- | :---------- |
| `MCP9001` | In place | The `EnumSchema` and `LegacyTitledEnumSchema` APIs are deprecated as of specification version 2025-11-25. Use the current schema APIs instead. |
| `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (e.g., `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. |
+| `MCP9003` | In place | The `RequestContext(McpServer, JsonRpcRequest)` constructor is obsolete. Use the overload that accepts a `parameters` argument: `RequestContext(McpServer, JsonRpcRequest, TParams)`. |
diff --git a/samples/EverythingServer/Program.cs b/samples/EverythingServer/Program.cs
index 99d7759..1807fa8 100644
--- a/samples/EverythingServer/Program.cs
+++ b/samples/EverythingServer/Program.cs
@@ -130,7 +130,7 @@ builder.Services
{
throw new McpException("Cannot add subscription for server with null SessionId");
}
- if (ctx.Params?.Uri is { } uri)
+ if (ctx.Params.Uri is { } uri)
{
subscriptions[ctx.Server.SessionId].TryAdd(uri, 0);
@@ -154,7 +154,7 @@ builder.Services
{
throw new McpException("Cannot remove subscription for server with null SessionId");
}
- if (ctx.Params?.Uri is { } uri)
+ if (ctx.Params.Uri is { } uri)
{
subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
}
@@ -212,11 +212,6 @@ builder.Services
})
.WithSetLoggingLevelHandler(async (ctx, ct) =>
{
- if (ctx.Params?.Level is null)
- {
- throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
- }
-
// The SDK updates the LoggingLevel field of the IMcpServer
await ctx.Server.SendNotificationAsync("notifications/message", new
diff --git a/samples/EverythingServer/Resources/SimpleResourceType.cs b/samples/EverythingServer/Resources/SimpleResourceType.cs
index eafc6c6..cc1aed7 100644
--- a/samples/EverythingServer/Resources/SimpleResourceType.cs
+++ b/samples/EverythingServer/Resources/SimpleResourceType.cs
@@ -19,7 +19,7 @@ public class SimpleResourceType
int index = id - 1;
if ((uint)index >= ResourceGenerator.Resources.Count)
{
- throw new NotSupportedException($"Unknown resource: {requestContext.Params?.Uri}");
+ throw new NotSupportedException($"Unknown resource: {requestContext.Params.Uri}");
}
var resource = ResourceGenerator.Resources[index];
diff --git a/samples/EverythingServer/Tools/LongRunningTool.cs b/samples/EverythingServer/Tools/LongRunningTool.cs
index 405b5e8..3dc42b5 100644
--- a/samples/EverythingServer/Tools/LongRunningTool.cs
+++ b/samples/EverythingServer/Tools/LongRunningTool.cs
@@ -15,7 +15,7 @@ public class LongRunningTool
int duration = 10,
int steps = 5)
{
- var progressToken = context.Params?.ProgressToken;
+ var progressToken = context.Params.ProgressToken;
var stepDuration = duration / steps;
for (int i = 1; i <= steps + 1; i++)
diff --git a/src/Common/Obsoletions.cs b/src/Common/Obsoletions.cs
index bbc7bff..cc850d1 100644
--- a/src/Common/Obsoletions.cs
+++ b/src/Common/Obsoletions.cs
@@ -25,4 +25,8 @@ internal static class Obsoletions
// MCP9002 was used for the AddXxxFilter extension methods on IMcpServerBuilder that were superseded by
// WithMessageFilters() and WithRequestFilters(). The APIs were removed; do not reuse this diagnostic ID.
+
+ public const string RequestContextParamsConstructor_DiagnosticId = "MCP9003";
+ public const string RequestContextParamsConstructor_Message = "Use the constructor overload that accepts a parameters argument.";
+ public const string RequestContextParamsConstructor_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcp9003";
}
diff --git a/src/ModelContextProtocol.Core/RequestHandlers.cs b/src/ModelContextProtocol.Core/RequestHandlers.cs
index fd95751..97e8b95 100644
--- a/src/ModelContextProtocol.Core/RequestHandlers.cs
+++ b/src/ModelContextProtocol.Core/RequestHandlers.cs
@@ -29,7 +29,7 @@ internal sealed class RequestHandlers : Dictionary
public void Set(
string method,
- Func> handler,
+ Func> handler,
JsonTypeInfo requestTypeInfo,
JsonTypeInfo responseTypeInfo)
{
@@ -40,7 +40,7 @@ internal sealed class RequestHandlers : Dictionary
{
- TParams? typedRequest = JsonSerializer.Deserialize(request.Params, requestTypeInfo);
+ TParams typedRequest = JsonSerializer.Deserialize(request.Params, requestTypeInfo)!;
object? result = await handler(typedRequest, request, cancellationToken).ConfigureAwait(false);
return JsonSerializer.SerializeToNode(result, responseTypeInfo);
};
diff --git a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs
index 3413d70..7f0f291 100644
--- a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs
+++ b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs
@@ -386,14 +386,14 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
TextContent tc => new()
{
- Contents = [new TextResourceContents { Uri = request.Params!.Uri, MimeType = ProtocolResourceTemplate.MimeType, Text = tc.Text }],
+ Contents = [new TextResourceContents { Uri = request.Params.Uri, MimeType = ProtocolResourceTemplate.MimeType, Text = tc.Text }],
},
DataContent dc => new()
{
Contents = [new BlobResourceContents
{
- Uri = request.Params!.Uri,
+ Uri = request.Params.Uri,
MimeType = dc.MediaType,
Blob = EncodingUtilities.GetUtf8Bytes(dc.Base64Data.Span)
}],
@@ -401,7 +401,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
string text => new()
{
- Contents = [new TextResourceContents { Uri = request.Params!.Uri, MimeType = ProtocolResourceTemplate.MimeType, Text = text }],
+ Contents = [new TextResourceContents { Uri = request.Params.Uri, MimeType = ProtocolResourceTemplate.MimeType, Text = text }],
},
IEnumerable contents => new()
@@ -416,14 +416,14 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
{
TextContent tc => new TextResourceContents
{
- Uri = request.Params!.Uri,
+ Uri = request.Params.Uri,
MimeType = ProtocolResourceTemplate.MimeType,
Text = tc.Text
},
DataContent dc => new BlobResourceContents
{
- Uri = request.Params!.Uri,
+ Uri = request.Params.Uri,
MimeType = dc.MediaType,
Blob = EncodingUtilities.GetUtf8Bytes(dc.Base64Data.Span)
},
@@ -436,7 +436,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
{
Contents = strings.Select(text => new TextResourceContents
{
- Uri = request.Params!.Uri,
+ Uri = request.Params.Uri,
MimeType = ProtocolResourceTemplate.MimeType,
Text = text
}).ToList(),
diff --git a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
index 753d916..c9a6f76 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
@@ -954,7 +954,7 @@ internal sealed partial class McpServerImpl : McpServer
// If a handler was provided, now delegate to it.
if (setLoggingLevelHandler is not null)
{
- return InvokeHandlerAsync(setLoggingLevelHandler, request, jsonRpcRequest, cancellationToken);
+ return InvokeHandlerAsync(setLoggingLevelHandler, request!, jsonRpcRequest, cancellationToken);
}
// Otherwise, consider it handled.
@@ -966,17 +966,17 @@ internal sealed partial class McpServerImpl : McpServer
private ValueTask InvokeHandlerAsync(
McpRequestHandler handler,
- TParams? args,
+ TParams args,
JsonRpcRequest jsonRpcRequest,
CancellationToken cancellationToken = default)
{
return _servicesScopePerRequest ?
InvokeScopedAsync(handler, args, jsonRpcRequest, cancellationToken) :
- handler(new(new DestinationBoundMcpServer(this, jsonRpcRequest.Context?.RelatedTransport), jsonRpcRequest) { Params = args }, cancellationToken);
+ handler(new(new DestinationBoundMcpServer(this, jsonRpcRequest.Context?.RelatedTransport), jsonRpcRequest, args), cancellationToken);
async ValueTask InvokeScopedAsync(
McpRequestHandler handler,
- TParams? args,
+ TParams args,
JsonRpcRequest jsonRpcRequest,
CancellationToken cancellationToken)
{
@@ -984,10 +984,9 @@ internal sealed partial class McpServerImpl : McpServer
try
{
return await handler(
- new RequestContext(new DestinationBoundMcpServer(this, jsonRpcRequest.Context?.RelatedTransport), jsonRpcRequest)
+ new RequestContext(new DestinationBoundMcpServer(this, jsonRpcRequest.Context?.RelatedTransport), jsonRpcRequest, args)
{
Services = scope?.ServiceProvider ?? Services,
- Params = args
},
cancellationToken).ConfigureAwait(false);
}
diff --git a/src/ModelContextProtocol.Core/Server/RequestContext.cs b/src/ModelContextProtocol.Core/Server/RequestContext.cs
index 959bb07..596712e 100644
--- a/src/ModelContextProtocol.Core/Server/RequestContext.cs
+++ b/src/ModelContextProtocol.Core/Server/RequestContext.cs
@@ -13,19 +13,34 @@ namespace ModelContextProtocol.Server;
///
public sealed class RequestContext : MessageContext
{
+ ///
+ /// Initializes a new instance of the class with the specified server, JSON-RPC request, and request parameters.
+ ///
+ /// The server with which this instance is associated.
+ /// The JSON-RPC request associated with this context.
+ /// The parameters associated with this request.
+ /// or is .
+ public RequestContext(McpServer server, JsonRpcRequest jsonRpcRequest, TParams parameters)
+ : base(server, jsonRpcRequest)
+ {
+ Params = parameters;
+ }
+
///
/// Initializes a new instance of the class with the specified server and JSON-RPC request.
///
/// The server with which this instance is associated.
/// The JSON-RPC request associated with this context.
/// or is .
+ [Obsolete(Obsoletions.RequestContextParamsConstructor_Message, DiagnosticId = Obsoletions.RequestContextParamsConstructor_DiagnosticId, UrlFormat = Obsoletions.RequestContextParamsConstructor_Url)]
public RequestContext(McpServer server, JsonRpcRequest jsonRpcRequest)
: base(server, jsonRpcRequest)
{
+ Params = default!;
}
/// Gets or sets the parameters associated with this request.
- public TParams? Params { get; set; }
+ public TParams Params { get; set; }
///
/// Gets or sets the primitive that matched the request.
diff --git a/tests/ModelContextProtocol.ConformanceServer/Program.cs b/tests/ModelContextProtocol.ConformanceServer/Program.cs
index 94c6272..017ec23 100644
--- a/tests/ModelContextProtocol.ConformanceServer/Program.cs
+++ b/tests/ModelContextProtocol.ConformanceServer/Program.cs
@@ -39,7 +39,7 @@ public class Program
// For the test_reconnection tool, enable polling mode after the tool runs.
// This stores the result and closes the SSE stream, so the client
// must reconnect via GET with Last-Event-ID to retrieve the result.
- if (request.Params?.Name == "test_reconnection")
+ if (request.Params.Name == "test_reconnection")
{
await request.EnablePollingAsync(TimeSpan.FromMilliseconds(500), cancellationToken);
}
@@ -54,7 +54,7 @@ public class Program
{
throw new McpException("Cannot add subscription for server with null SessionId");
}
- if (ctx.Params?.Uri is { } uri)
+ if (ctx.Params.Uri is { } uri)
{
var sessionSubscriptions = subscriptions.GetOrAdd(ctx.Server.SessionId, _ => new());
sessionSubscriptions.TryAdd(uri, 0);
@@ -68,7 +68,7 @@ public class Program
{
throw new McpException("Cannot remove subscription for server with null SessionId");
}
- if (ctx.Params?.Uri is { } uri)
+ if (ctx.Params.Uri is { } uri)
{
subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
}
@@ -91,11 +91,6 @@ public class Program
})
.WithSetLoggingLevelHandler(async (ctx, ct) =>
{
- if (ctx.Params?.Level is null)
- {
- throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
- }
-
// The SDK updates the LoggingLevel field of the McpServer
// Send a log notification to confirm the level was set
await ctx.Server.SendNotificationAsync("notifications/message", new LoggingMessageNotificationParams
diff --git a/tests/ModelContextProtocol.ConformanceServer/Tools/ConformanceTools.cs b/tests/ModelContextProtocol.ConformanceServer/Tools/ConformanceTools.cs
index 0a1f7ec..d6db6f6 100644
--- a/tests/ModelContextProtocol.ConformanceServer/Tools/ConformanceTools.cs
+++ b/tests/ModelContextProtocol.ConformanceServer/Tools/ConformanceTools.cs
@@ -132,7 +132,7 @@ public class ConformanceTools
RequestContext context,
CancellationToken cancellationToken)
{
- var progressToken = context.Params?.ProgressToken;
+ var progressToken = context.Params.ProgressToken;
if (progressToken is not null)
{
diff --git a/tests/ModelContextProtocol.TestServer/Program.cs b/tests/ModelContextProtocol.TestServer/Program.cs
index 89ad998..9cb963a 100644
--- a/tests/ModelContextProtocol.TestServer/Program.cs
+++ b/tests/ModelContextProtocol.TestServer/Program.cs
@@ -204,9 +204,9 @@ internal static class Program
};
options.Handlers.CallToolHandler = async (request, cancellationToken) =>
{
- if (request.Params?.Name == "echo")
+ if (request.Params.Name == "echo")
{
- if (request.Params?.Arguments is null || !request.Params.Arguments.TryGetValue("message", out var message))
+ if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("message", out var message))
{
throw new McpProtocolException("Missing required argument 'message'", McpErrorCode.InvalidParams);
}
@@ -215,16 +215,16 @@ internal static class Program
Content = [new TextContentBlock { Text = $"Echo: {message}" }]
};
}
- else if (request.Params?.Name == "echoSessionId")
+ else if (request.Params.Name == "echoSessionId")
{
return new CallToolResult
{
Content = [new TextContentBlock { Text = request.Server.SessionId ?? string.Empty }]
};
}
- else if (request.Params?.Name == "trigger-sampling-request")
+ else if (request.Params.Name == "trigger-sampling-request")
{
- if (request.Params?.Arguments is null ||
+ if (request.Params.Arguments is null ||
!request.Params.Arguments.TryGetValue("prompt", out var prompt) ||
!request.Params.Arguments.TryGetValue("maxTokens", out var maxTokens))
{
@@ -238,16 +238,16 @@ internal static class Program
Content = [new TextContentBlock { Text = $"LLM sampling result: {sampleResult.Content.OfType().FirstOrDefault()?.Text}" }]
};
}
- else if (request.Params?.Name == "echoCliArg")
+ else if (request.Params.Name == "echoCliArg")
{
return new CallToolResult
{
Content = [new TextContentBlock { Text = cliArg ?? "null" }]
};
}
- else if (request.Params?.Name == "longRunning")
+ else if (request.Params.Name == "longRunning")
{
- if (request.Params?.Arguments is null || !request.Params.Arguments.TryGetValue("durationMs", out var durationMsValue))
+ if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("durationMs", out var durationMsValue))
{
throw new McpProtocolException("Missing required argument 'durationMs'", McpErrorCode.InvalidParams);
}
@@ -258,9 +258,9 @@ internal static class Program
Content = [new TextContentBlock { Text = $"Long-running operation completed after {durationMs}ms" }]
};
}
- else if (request.Params?.Name == "crash")
+ else if (request.Params.Name == "crash")
{
- if (request.Params?.Arguments is null || !request.Params.Arguments.TryGetValue("exitCode", out var exitCodeValue))
+ if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("exitCode", out var exitCodeValue))
{
throw new McpProtocolException("Missing required argument 'exitCode'", McpErrorCode.InvalidParams);
}
@@ -271,7 +271,7 @@ internal static class Program
}
else
{
- throw new McpProtocolException($"Unknown tool: {request.Params?.Name}", McpErrorCode.InvalidParams);
+ throw new McpProtocolException($"Unknown tool: {request.Params.Name}", McpErrorCode.InvalidParams);
}
};
}
@@ -335,7 +335,7 @@ internal static class Program
options.Handlers.GetPromptHandler = async (request, cancellationToken) =>
{
List messages = [];
- if (request.Params?.Name == "simple-prompt")
+ if (request.Params.Name == "simple-prompt")
{
messages.Add(new PromptMessage
{
@@ -343,7 +343,7 @@ internal static class Program
Content = new TextContentBlock { Text = "This is a simple prompt without arguments." },
});
}
- else if (request.Params?.Name == "args-prompt")
+ else if (request.Params.Name == "args-prompt")
{
string city = request.Params.Arguments?["city"].ToString() ?? "unknown";
string state = request.Params.Arguments?["state"].ToString() ?? "";
@@ -354,7 +354,7 @@ internal static class Program
Content = new TextContentBlock { Text = $"What's weather in {location}?" },
});
}
- else if (request.Params?.Name == "completable-prompt")
+ else if (request.Params.Name == "completable-prompt")
{
string department = request.Params.Arguments?["department"].ToString() ?? "unknown";
string name = request.Params.Arguments?["name"].ToString() ?? "unknown";
@@ -366,7 +366,7 @@ internal static class Program
}
else
{
- throw new McpProtocolException($"Unknown prompt: {request.Params?.Name}", McpErrorCode.InvalidParams);
+ throw new McpProtocolException($"Unknown prompt: {request.Params.Name}", McpErrorCode.InvalidParams);
}
return new GetPromptResult
@@ -382,11 +382,6 @@ internal static class Program
{
options.Handlers.SetLoggingLevelHandler = async (request, cancellationToken) =>
{
- if (request.Params?.Level is null)
- {
- throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
- }
-
_minimumLoggingLevel = request.Params.Level;
return new EmptyResult();
@@ -452,7 +447,7 @@ internal static class Program
options.Handlers.ListResourcesHandler = async (request, cancellationToken) =>
{
int startIndex = 0;
- if (request.Params?.Cursor is not null)
+ if (request.Params.Cursor is not null)
{
try
{
@@ -481,7 +476,7 @@ internal static class Program
options.Handlers.ReadResourceHandler = async (request, cancellationToken) =>
{
- if (request.Params?.Uri is null)
+ if (request.Params.Uri is null)
{
throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams);
}
@@ -518,7 +513,7 @@ internal static class Program
options.Handlers.SubscribeToResourcesHandler = async (request, cancellationToken) =>
{
- if (request?.Params?.Uri is null)
+ if (request?.Params.Uri is null)
{
throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams);
}
@@ -535,7 +530,7 @@ internal static class Program
options.Handlers.UnsubscribeFromResourcesHandler = async (request, cancellationToken) =>
{
- if (request?.Params?.Uri is null)
+ if (request?.Params.Uri is null)
{
throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams);
}
@@ -563,7 +558,7 @@ internal static class Program
options.Handlers.CompleteHandler = async (request, cancellationToken) =>
{
string[]? values;
- switch (request.Params?.Ref)
+ switch (request.Params.Ref)
{
case ResourceTemplateReference rtr:
var resourceId = rtr.Uri?.Split('/').LastOrDefault();
@@ -571,7 +566,7 @@ internal static class Program
return new CompleteResult { Completion = new() { Values = [] } };
// Filter resource IDs that start with the input value
- values = sampleResourceIds.Where(id => id.StartsWith(request.Params!.Argument.Value)).ToArray();
+ values = sampleResourceIds.Where(id => id.StartsWith(request.Params.Argument.Value)).ToArray();
return new CompleteResult { Completion = new() { Values = values, HasMore = false, Total = values.Length } };
case PromptReference pr:
@@ -583,7 +578,7 @@ internal static class Program
return new CompleteResult { Completion = new() { Values = values, HasMore = false, Total = values.Length } };
default:
- throw new McpProtocolException($"Unknown reference type: '{request.Params?.Ref.Type}'", McpErrorCode.InvalidParams);
+ throw new McpProtocolException($"Unknown reference type: '{request.Params.Ref.Type}'", McpErrorCode.InvalidParams);
}
};
}
diff --git a/tests/ModelContextProtocol.TestSseServer/Program.cs b/tests/ModelContextProtocol.TestSseServer/Program.cs
index 1a27c0c..3c42e29 100644
--- a/tests/ModelContextProtocol.TestSseServer/Program.cs
+++ b/tests/ModelContextProtocol.TestSseServer/Program.cs
@@ -280,7 +280,7 @@ public class Program
ReadResourceHandler = async (request, cancellationToken) =>
{
- if (request.Params?.Uri is null)
+ if (request.Params.Uri is null)
{
throw new McpProtocolException("Missing required argument 'uri'", McpErrorCode.InvalidParams);
}
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientMetaTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientMetaTests.cs
index ddb30b7..863d8e6 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientMetaTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientMetaTests.cs
@@ -37,7 +37,7 @@ public class McpClientMetaTests : ClientServerTestBase
async (RequestContext context) =>
{
// Access the foo property of _meta field from the request parameters
- var metaFoo = context.Params?.Meta?["foo"]?.ToString();
+ var metaFoo = context.Params.Meta?["foo"]?.ToString();
// Assert that the meta foo is correctly passed
Assert.NotNull(metaFoo);
@@ -73,7 +73,7 @@ public class McpClientMetaTests : ClientServerTestBase
(RequestContext context) =>
{
// Access the foo property of _meta field from the request parameters
- var metaFoo = context.Params?.Meta?["foo"]?.ToString();
+ var metaFoo = context.Params.Meta?["foo"]?.ToString();
// Assert that the meta foo is correctly passed
Assert.NotNull(metaFoo);
@@ -109,7 +109,7 @@ public class McpClientMetaTests : ClientServerTestBase
(RequestContext context) =>
{
// Access the foo property of _meta field from the request parameters
- var metaFoo = context.Params?.Meta?["foo"]?.ToString();
+ var metaFoo = context.Params.Meta?["foo"]?.ToString();
// Assert that the meta foo is correctly passed
Assert.NotNull(metaFoo);
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientPromptTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientPromptTests.cs
index ec9f5f5..e84b987 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientPromptTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientPromptTests.cs
@@ -29,7 +29,7 @@ public class McpClientPromptTests : ClientServerTestBase
[McpServerPrompt, Description("Echoes back the metadata it receives")]
public static ChatMessage MetadataEcho(RequestContext context) =>
- new(ChatRole.User, context.Params?.Meta?.ToJsonString() ?? "{}");
+ new(ChatRole.User, context.Params.Meta?.ToJsonString() ?? "{}");
}
[Fact]
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateConstructorTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateConstructorTests.cs
index efaa647..6a415dd 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateConstructorTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateConstructorTests.cs
@@ -27,7 +27,7 @@ public class McpClientResourceTemplateConstructorTests : ClientServerTestBase
[McpServerResource, Description("Echoes back the metadata it receives")]
public static string MetadataEcho(RequestContext context, [Description("An ID")] string id) =>
- context.Params?.Meta?.ToJsonString() ?? "{}";
+ context.Params.Meta?.ToJsonString() ?? "{}";
}
[Fact]
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateTests.cs
index 693dab2..9bafd8e 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateTests.cs
@@ -17,7 +17,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
mcpServerBuilder.WithReadResourceHandler((request, cancellationToken) =>
new ValueTask(new ReadResourceResult
{
- Contents = [new TextResourceContents { Text = request.Params?.Uri ?? "", Uri = request.Params?.Uri ?? "" }]
+ Contents = [new TextResourceContents { Text = request.Params.Uri ?? "", Uri = request.Params.Uri ?? "" }]
}));
}
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientResourceTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientResourceTests.cs
index 52fe54b..26e5f1d 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientResourceTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientResourceTests.cs
@@ -27,7 +27,7 @@ public class McpClientResourceTests : ClientServerTestBase
[McpServerResource, Description("Echoes back the metadata it receives")]
public static string MetadataEcho(RequestContext context) =>
- context.Params?.Meta?.ToJsonString() ?? "{}";
+ context.Params.Meta?.ToJsonString() ?? "{}";
}
[Fact]
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs
index e17fb6b..4ec64da 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs
@@ -161,7 +161,7 @@ public class McpClientToolTests : ClientServerTestBase
[McpServerTool]
public static TextContentBlock MetadataEchoTool(RequestContext context)
{
- var meta = context.Params?.Meta;
+ var meta = context.Params.Meta;
var metaJson = meta?.ToJsonString() ?? "{}";
return new TextContentBlock { Text = metaJson };
}
diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs
index 13744ff..171c6be 100644
--- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsMessageFilterTests.cs
@@ -826,7 +826,7 @@ public class McpServerBuilderExtensionsMessageFilterTests(ITestOutputHelper test
RequestContext context,
CancellationToken cancellationToken)
{
- if (context.Params?.ProgressToken is { } token)
+ if (context.Params.ProgressToken is { } token)
{
await server.NotifyProgressAsync(token, new ProgressNotificationValue
{
diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsPromptsTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsPromptsTests.cs
index 69405e1..209469e 100644
--- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsPromptsTests.cs
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsPromptsTests.cs
@@ -26,7 +26,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
mcpServerBuilder
.WithListPromptsHandler(async (request, cancellationToken) =>
{
- var cursor = request.Params?.Cursor;
+ var cursor = request.Params.Cursor;
switch (cursor)
{
case null:
@@ -68,7 +68,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
})
.WithGetPromptHandler(async (request, cancellationToken) =>
{
- switch (request.Params?.Name)
+ switch (request.Params.Name)
{
case "FirstCustomPrompt":
case "SecondCustomPrompt":
@@ -79,7 +79,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
};
default:
- throw new McpProtocolException($"Unknown prompt '{request.Params?.Name}'", McpErrorCode.InvalidParams);
+ throw new McpProtocolException($"Unknown prompt '{request.Params.Name}'", McpErrorCode.InvalidParams);
}
})
.WithPrompts();
@@ -314,17 +314,14 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
sc.AddMcpServer().WithPrompts(target);
McpServerPrompt prompt = sc.BuildServiceProvider().GetServices().First(t => t.ProtocolPrompt.Name == "returns_string");
- var result = await prompt.GetAsync(new RequestContext(new Mock().Object, new JsonRpcRequest { Method = "test", Id = new RequestId("1") })
+ var result = await prompt.GetAsync(new RequestContext(new Mock().Object, new JsonRpcRequest { Method = "test", Id = new RequestId("1") }, new GetPromptRequestParams
{
- Params = new GetPromptRequestParams
+ Name = "returns_string",
+ Arguments = new Dictionary
{
- Name = "returns_string",
- Arguments = new Dictionary
- {
- ["message"] = JsonSerializer.SerializeToElement("hello", AIJsonUtilities.DefaultOptions),
- }
+ ["message"] = JsonSerializer.SerializeToElement("hello", AIJsonUtilities.DefaultOptions),
}
- }, TestContext.Current.CancellationToken);
+ }), TestContext.Current.CancellationToken);
Assert.Equal(target.ReturnsString("hello"), (result.Messages[0].Content as TextContentBlock)?.Text);
}
diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs
index 545384a..4b03cad 100644
--- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs
@@ -25,7 +25,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
mcpServerBuilder
.WithListResourcesHandler(async (request, cancellationToken) =>
{
- var cursor = request.Params?.Cursor;
+ var cursor = request.Params.Cursor;
switch (cursor)
{
case null:
@@ -67,7 +67,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
})
.WithListResourceTemplatesHandler(async (request, cancellationToken) =>
{
- var cursor = request.Params?.Cursor;
+ var cursor = request.Params.Cursor;
switch (cursor)
{
case null:
@@ -96,7 +96,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
})
.WithReadResourceHandler(async (request, cancellationToken) =>
{
- switch (request.Params?.Uri)
+ switch (request.Params.Uri)
{
case "test://Resource1":
case "test://Resource2":
@@ -105,11 +105,11 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
case "test://ResourceTemplate2":
return new ReadResourceResult
{
- Contents = [new TextResourceContents { Text = request.Params?.Uri ?? "(null)", Uri = request.Params?.Uri ?? "(null)" }]
+ Contents = [new TextResourceContents { Text = request.Params.Uri ?? "(null)", Uri = request.Params.Uri ?? "(null)" }]
};
}
- throw new McpProtocolException($"Resource not found: {request.Params?.Uri}", McpErrorCode.ResourceNotFound);
+ throw new McpProtocolException($"Resource not found: {request.Params.Uri}", McpErrorCode.ResourceNotFound);
})
.WithResources();
}
@@ -345,13 +345,10 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
sc.AddMcpServer().WithResources(target);
McpServerResource resource = sc.BuildServiceProvider().GetServices().First(t => t.ProtocolResource?.Name == "returns_string");
- var result = await resource.ReadAsync(new RequestContext(new Mock().Object, new JsonRpcRequest { Method = "test", Id = new RequestId("1") })
+ var result = await resource.ReadAsync(new RequestContext(new Mock().Object, new JsonRpcRequest { Method = "test", Id = new RequestId("1") }, new()
{
- Params = new()
- {
- Uri = "returns://string"
- }
- }, TestContext.Current.CancellationToken);
+ Uri = "returns://string"
+ }), TestContext.Current.CancellationToken);
Assert.Equal(target.ReturnsString(), (result?.Contents[0] as TextResourceContents)?.Text);
}
diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsToolsTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsToolsTests.cs
index 518b70f..d2db4c6 100644
--- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsToolsTests.cs
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsToolsTests.cs
@@ -30,7 +30,7 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
mcpServerBuilder
.WithListToolsHandler(async (request, cancellationToken) =>
{
- var cursor = request.Params?.Cursor;
+ var cursor = request.Params.Cursor;
switch (cursor)
{
case null:
@@ -93,7 +93,7 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
})
.WithCallToolHandler(async (request, cancellationToken) =>
{
- switch (request.Params?.Name)
+ switch (request.Params.Name)
{
case "FirstCustomTool":
case "SecondCustomTool":
@@ -104,7 +104,7 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
};
default:
- throw new McpProtocolException($"Unknown tool: '{request.Params?.Name}'", McpErrorCode.InvalidParams);
+ throw new McpProtocolException($"Unknown tool: '{request.Params.Name}'", McpErrorCode.InvalidParams);
}
})
.WithTools(serializerOptions: BuilderToolsJsonContext.Default.Options);
@@ -594,7 +594,7 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
sc.AddMcpServer().WithTools(target, BuilderToolsJsonContext.Default.Options);
McpServerTool tool = sc.BuildServiceProvider().GetServices().First(t => t.ProtocolTool.Name == "get_ctor_parameter");
- var result = await tool.InvokeAsync(new RequestContext(new Mock().Object, new JsonRpcRequest { Method = "test", Id = new RequestId("1") }), TestContext.Current.CancellationToken);
+ var result = await tool.InvokeAsync(new RequestContext(new Mock().Object, new JsonRpcRequest { Method = "test", Id = new RequestId("1") }, new() { Name = "" }), TestContext.Current.CancellationToken);
Assert.Equal(target.GetCtorParameter(), (result.Content[0] as TextContentBlock)?.Text);
}
diff --git a/tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs b/tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs
index 6816f55..7d50a30 100644
--- a/tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs
+++ b/tests/ModelContextProtocol.Tests/McpProtocolExceptionDataTests.cs
@@ -28,7 +28,7 @@ public class McpProtocolExceptionDataTests : ClientServerTestBase
{
mcpServerBuilder.WithCallToolHandler((request, cancellationToken) =>
{
- var toolName = request.Params?.Name;
+ var toolName = request.Params.Name;
switch (toolName)
{
diff --git a/tests/ModelContextProtocol.Tests/Protocol/ElicitationTypedTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ElicitationTypedTests.cs
index 6f0988e..7b55b73 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/ElicitationTypedTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/ElicitationTypedTests.cs
@@ -19,7 +19,7 @@ public partial class ElicitationTypedTests : ClientServerTestBase
{
Assert.NotNull(request.Params);
- if (request.Params!.Name == "TestElicitationTyped")
+ if (request.Params.Name == "TestElicitationTyped")
{
var result = await request.Server.ElicitAsync(
message: "Please provide more information.",
@@ -34,7 +34,7 @@ public partial class ElicitationTypedTests : ClientServerTestBase
Assert.Equal(SampleRole.Admin, result.Content!.Role);
Assert.Equal(99.5, result.Content!.Score);
}
- else if (request.Params!.Name == "TestElicitationCamelForm")
+ else if (request.Params.Name == "TestElicitationCamelForm")
{
var result = await request.Server.ElicitAsync(
message: "Please provide more information.",
@@ -47,7 +47,7 @@ public partial class ElicitationTypedTests : ClientServerTestBase
Assert.Equal(90210, result.Content!.ZipCode);
Assert.False(result.Content!.IsAdmin);
}
- else if (request.Params!.Name == "TestElicitationNullablePropertyForm")
+ else if (request.Params.Name == "TestElicitationNullablePropertyForm")
{
var result = await request.Server.ElicitAsync(
message: "Please provide more information.",
@@ -60,7 +60,7 @@ public partial class ElicitationTypedTests : ClientServerTestBase
Content = [new TextContentBlock { Text = "unexpected" }],
};
}
- else if (request.Params!.Name == "TestElicitationUnsupportedType")
+ else if (request.Params.Name == "TestElicitationUnsupportedType")
{
await request.Server.ElicitAsync(
message: "Please provide more information.",
@@ -73,7 +73,7 @@ public partial class ElicitationTypedTests : ClientServerTestBase
Content = [new TextContentBlock { Text = "unexpected" }],
};
}
- else if (request.Params!.Name == "TestElicitationNonObjectGenericType")
+ else if (request.Params.Name == "TestElicitationNonObjectGenericType")
{
// This should throw because T is not an object type with properties (string primitive)
await request.Server.ElicitAsync(
@@ -86,7 +86,7 @@ public partial class ElicitationTypedTests : ClientServerTestBase
Content = [new TextContentBlock { Text = "unexpected" }],
};
}
- else if (request.Params!.Name == "TestElicitationWithDefaults")
+ else if (request.Params.Name == "TestElicitationWithDefaults")
{
var result = await request.Server.ElicitAsync(
message: "Please provide information.",
@@ -101,7 +101,7 @@ public partial class ElicitationTypedTests : ClientServerTestBase
}
else
{
- Assert.Fail($"Unexpected tool name: {request.Params!.Name}");
+ Assert.Fail($"Unexpected tool name: {request.Params.Name}");
}
return new CallToolResult
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerPromptTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerPromptTests.cs
index b463514..dec16d6 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerPromptTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerPromptTests.cs
@@ -54,7 +54,7 @@ public class McpServerPromptTests
Assert.DoesNotContain("server", prompt.ProtocolPrompt.Arguments?.Select(a => a.Name) ?? []);
var result = await prompt.GetAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()),
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.NotNull(result.Messages);
@@ -83,7 +83,7 @@ public class McpServerPromptTests
}, new() { Services = services });
var result = await prompt.GetAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()),
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.NotNull(result.Messages);
@@ -133,11 +133,11 @@ public class McpServerPromptTests
Assert.DoesNotContain("actualMyService", prompt.ProtocolPrompt.Arguments?.Select(a => a.Name) ?? []);
await Assert.ThrowsAnyAsync(async () => await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken));
var result = await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Services = services },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }) { Services = services },
TestContext.Current.CancellationToken);
Assert.Equal("Hello", Assert.IsType(result.Messages[0].Content).Text);
}
@@ -158,7 +158,7 @@ public class McpServerPromptTests
}, new() { Services = services });
var result = await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal("Hello", Assert.IsType(result.Messages[0].Content).Text);
}
@@ -171,7 +171,7 @@ public class McpServerPromptTests
_ => new DisposablePromptType());
var result = await prompt1.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal("disposals:1", Assert.IsType(result.Messages[0].Content).Text);
}
@@ -184,7 +184,7 @@ public class McpServerPromptTests
_ => new AsyncDisposablePromptType());
var result = await prompt1.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal("asyncDisposals:1", Assert.IsType(result.Messages[0].Content).Text);
}
@@ -197,7 +197,7 @@ public class McpServerPromptTests
_ => new AsyncDisposableAndDisposablePromptType());
var result = await prompt1.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal("disposals:0, asyncDisposals:1", Assert.IsType(result.Messages[0].Content).Text);
}
@@ -213,7 +213,7 @@ public class McpServerPromptTests
});
var actual = await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Same(expected, actual);
@@ -230,7 +230,7 @@ public class McpServerPromptTests
});
var actual = await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.NotNull(actual);
@@ -256,7 +256,7 @@ public class McpServerPromptTests
});
var actual = await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.NotNull(actual);
@@ -288,7 +288,7 @@ public class McpServerPromptTests
});
var actual = await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.NotNull(actual);
@@ -315,7 +315,7 @@ public class McpServerPromptTests
});
var actual = await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.NotNull(actual);
@@ -347,7 +347,7 @@ public class McpServerPromptTests
});
var actual = await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.NotNull(actual);
@@ -368,7 +368,7 @@ public class McpServerPromptTests
});
await Assert.ThrowsAsync(async () => await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken));
}
@@ -381,7 +381,7 @@ public class McpServerPromptTests
});
await Assert.ThrowsAsync(async () => await prompt.GetAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken));
}
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs
index 695bbf3..dfcc0bd 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs
@@ -51,7 +51,7 @@ public partial class McpServerResourceTests
{
Contents = [new TextResourceContents
{
- Uri = ctx.Params!.Uri!,
+ Uri = ctx.Params.Uri!,
Text = "Static Resource",
MimeType = "text/plain",
}]
@@ -87,7 +87,7 @@ public partial class McpServerResourceTests
{
Contents = [new TextResourceContents
{
- Uri = ctx.Params!.Uri!,
+ Uri = ctx.Params.Uri!,
Text = "Static Resource",
MimeType = "text/plain",
}]
@@ -111,7 +111,7 @@ public partial class McpServerResourceTests
{
Contents = [new TextResourceContents
{
- Uri = ctx.Params!.Uri!,
+ Uri = ctx.Params.Uri!,
Text = "Static Resource",
MimeType = "text/plain",
}]
@@ -148,7 +148,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create(() => "42", new() { Name = Name });
Assert.Equal("resource://mcp/Hello", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Hello" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Hello" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("42", ((TextResourceContents)result.Contents[0]).Text);
@@ -156,7 +156,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((McpServer server) => "42", new() { Name = Name });
Assert.Equal("resource://mcp/Hello", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Hello" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Hello" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("42", ((TextResourceContents)result.Contents[0]).Text);
@@ -164,7 +164,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((string arg1) => arg1, new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?arg1}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?arg1=wOrLd" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?arg1=wOrLd" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("wOrLd", ((TextResourceContents)result.Contents[0]).Text);
@@ -172,7 +172,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((string arg1, string? arg2 = null) => arg1 + arg2, new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?arg1,arg2}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?arg1=wo&arg2=rld" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?arg1=wo&arg2=rld" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("world", ((TextResourceContents)result.Contents[0]).Text);
@@ -180,7 +180,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((object a1, bool a2, char a3, byte a4, sbyte a5) => a1.ToString() + a2 + a3 + a4 + a5, new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1=hi&a2=true&a3=s&a4=12&a5=34" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1=hi&a2=true&a3=s&a4=12&a5=34" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("hiTrues1234", ((TextResourceContents)result.Contents[0]).Text);
@@ -188,7 +188,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((ushort a1, short a2, uint a3, int a4, ulong a5) => (a1 + a2 + a3 + a4 + (long)a5).ToString(), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1=10&a2=20&a3=30&a4=40&a5=50" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1=10&a2=20&a3=30&a4=40&a5=50" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("150", ((TextResourceContents)result.Contents[0]).Text);
@@ -196,7 +196,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((long a1, float a2, double a3, decimal a4, TimeSpan a5) => a5.ToString(), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1=1&a2=2&a3=3&a4=4&a5=5" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1=1&a2=2&a3=3&a4=4&a5=5" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("5.00:00:00", ((TextResourceContents)result.Contents[0]).Text);
@@ -204,7 +204,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((DateTime a1, DateTimeOffset a2, Uri a3, Guid a4, Version a5) => a4.ToString("N") + a5, new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1={DateTime.UtcNow:r}&a2={DateTimeOffset.UtcNow:r}&a3=http%3A%2F%2Ftest&a4=14e5f43d-0d41-47d6-8207-8249cf669e41&a5=1.2.3.4" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1={DateTime.UtcNow:r}&a2={DateTimeOffset.UtcNow:r}&a3=http%3A%2F%2Ftest&a4=14e5f43d-0d41-47d6-8207-8249cf669e41&a5=1.2.3.4" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("14e5f43d0d4147d682078249cf669e411.2.3.4", ((TextResourceContents)result.Contents[0]).Text);
@@ -213,7 +213,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((Half a2, Int128 a3, UInt128 a4, IntPtr a5) => (a3 + (Int128)a4 + a5).ToString(), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a2=1.0&a3=3&a4=4&a5=5" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a2=1.0&a3=3&a4=4&a5=5" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("12", ((TextResourceContents)result.Contents[0]).Text);
@@ -221,7 +221,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((UIntPtr a1, DateOnly a2, TimeOnly a3) => a1.ToString(), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a3}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1=123&a2=0001-02-03&a3=01%3A02%3A03" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1=123&a2=0001-02-03&a3=01%3A02%3A03" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("123", ((TextResourceContents)result.Contents[0]).Text);
@@ -230,7 +230,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((bool? a2, char? a3, byte? a4, sbyte? a5) => a2?.ToString() + a3 + a4 + a5, new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a2=true&a3=s&a4=12&a5=34" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a2=true&a3=s&a4=12&a5=34" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("Trues1234", ((TextResourceContents)result.Contents[0]).Text);
@@ -238,7 +238,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((ushort? a1, short? a2, uint? a3, int? a4, ulong? a5) => (a1 + a2 + a3 + a4 + (long?)a5).ToString(), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1=10&a2=20&a3=30&a4=40&a5=50" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1=10&a2=20&a3=30&a4=40&a5=50" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("150", ((TextResourceContents)result.Contents[0]).Text);
@@ -246,7 +246,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((long? a1, float? a2, double? a3, decimal? a4, TimeSpan? a5) => a5?.ToString(), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1=1&a2=2&a3=3&a4=4&a5=5" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1=1&a2=2&a3=3&a4=4&a5=5" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("5.00:00:00", ((TextResourceContents)result.Contents[0]).Text);
@@ -254,7 +254,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((DateTime? a1, DateTimeOffset? a2, Guid? a4) => a4?.ToString("N"), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a4}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1={DateTime.UtcNow:r}&a2={DateTimeOffset.UtcNow:r}&a4=14e5f43d-0d41-47d6-8207-8249cf669e41" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1={DateTime.UtcNow:r}&a2={DateTimeOffset.UtcNow:r}&a4=14e5f43d-0d41-47d6-8207-8249cf669e41" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("14e5f43d0d4147d682078249cf669e41", ((TextResourceContents)result.Contents[0]).Text);
@@ -263,7 +263,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((Half? a2, Int128? a3, UInt128? a4, IntPtr? a5) => (a3 + (Int128?)a4 + a5).ToString(), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a2,a3,a4,a5}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a2=1.0&a3=3&a4=4&a5=5" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a2=1.0&a3=3&a4=4&a5=5" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("12", ((TextResourceContents)result.Contents[0]).Text);
@@ -271,7 +271,7 @@ public partial class McpServerResourceTests
t = McpServerResource.Create((UIntPtr? a1, DateOnly? a2, TimeOnly? a3) => a1?.ToString(), new() { Name = Name });
Assert.Equal($"resource://mcp/Hello{{?a1,a2,a3}}", t.ProtocolResourceTemplate.UriTemplate);
result = await t.ReadAsync(
- new RequestContext(server, CreateTestJsonRpcRequest()) { Params = new() { Uri = $"resource://mcp/Hello?a1=123&a2=0001-02-03&a3=01%3A02%3A03" } },
+ new RequestContext(server, CreateTestJsonRpcRequest(), new() { Uri = $"resource://mcp/Hello?a1=123&a2=0001-02-03&a3=01%3A02%3A03" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("123", ((TextResourceContents)result.Contents[0]).Text);
@@ -288,7 +288,7 @@ public partial class McpServerResourceTests
Assert.Equal("resource://mcp/Hello{?arg1}", t.ProtocolResourceTemplate.UriTemplate);
Assert.False(t.IsMatch(uri));
await Assert.ThrowsAsync(async () => await t.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = uri } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = uri }),
TestContext.Current.CancellationToken));
}
@@ -299,7 +299,7 @@ public partial class McpServerResourceTests
{
McpServerResource t = McpServerResource.Create(() => "resource", new() { UriTemplate = actualUri });
Assert.NotNull(await t.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = queriedUri } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = queriedUri }),
TestContext.Current.CancellationToken));
}
@@ -328,7 +328,7 @@ public partial class McpServerResourceTests
McpServerResource t = McpServerResource.Create((string arg1, int arg2) => arg1, new() { Name = "Hello" });
Assert.Equal("resource://mcp/Hello{?arg1,arg2}", t.ProtocolResourceTemplate.UriTemplate);
await Assert.ThrowsAsync(async () => await t.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = uri } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = uri }),
TestContext.Current.CancellationToken));
}
@@ -341,25 +341,25 @@ public partial class McpServerResourceTests
ReadResourceResult result;
result = await t.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Hello" } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Hello" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("", ((TextResourceContents)result.Contents[0]).Text);
result = await t.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Hello?arg1=first" } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Hello?arg1=first" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("first", ((TextResourceContents)result.Contents[0]).Text);
result = await t.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Hello?arg2=42" } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Hello?arg2=42" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("42", ((TextResourceContents)result.Contents[0]).Text);
result = await t.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Hello?arg1=first&arg2=42" } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Hello?arg1=first&arg2=42" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("first42", ((TextResourceContents)result.Contents[0]).Text);
@@ -377,7 +377,7 @@ public partial class McpServerResourceTests
}, new() { Name = "Test" });
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("42", ((TextResourceContents)result.Contents[0]).Text);
@@ -404,7 +404,7 @@ public partial class McpServerResourceTests
}, new() { Services = services });
var result = await tool.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "https://something" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "https://something" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.NotNull(result.Contents);
@@ -481,11 +481,11 @@ public partial class McpServerResourceTests
Mock mockServer = new();
await Assert.ThrowsAnyAsync(async () => await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken));
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Services = services, Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }) { Services = services },
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("42", ((TextResourceContents)result.Contents[0]).Text);
@@ -507,7 +507,7 @@ public partial class McpServerResourceTests
}, new() { Services = services, Name = "Test" });
var result = await resource.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("42", ((TextResourceContents)result.Contents[0]).Text);
@@ -523,7 +523,7 @@ public partial class McpServerResourceTests
_ => new DisposableResourceType());
var result = await resource1.ReadAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "test://static/resource/instanceMethod" } },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Uri = "test://static/resource/instanceMethod" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal("0", ((TextResourceContents)result.Contents[0]).Text);
@@ -541,7 +541,7 @@ public partial class McpServerResourceTests
return new ReadResourceResult { Contents = [new TextResourceContents { Text = "hello", Uri = "" }] };
}, new() { Name = "Test" });
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Single(result.Contents);
@@ -558,7 +558,7 @@ public partial class McpServerResourceTests
return new TextResourceContents { Text = "hello", Uri = "" };
}, new() { Name = "Test", SerializerOptions = JsonContext6.Default.Options });
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Single(result.Contents);
@@ -579,7 +579,7 @@ public partial class McpServerResourceTests
];
}, new() { Name = "Test" });
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal(2, result.Contents.Count);
@@ -597,7 +597,7 @@ public partial class McpServerResourceTests
return "42";
}, new() { Name = "Test" });
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Single(result.Contents);
@@ -614,7 +614,7 @@ public partial class McpServerResourceTests
return new List { "42", "43" };
}, new() { Name = "Test", SerializerOptions = JsonContext6.Default.Options });
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal(2, result.Contents.Count);
@@ -632,7 +632,7 @@ public partial class McpServerResourceTests
return new DataContent(new byte[] { 0, 1, 2 }, "application/octet-stream");
}, new() { Name = "Test" });
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Single(result.Contents);
@@ -654,7 +654,7 @@ public partial class McpServerResourceTests
};
}, new() { Name = "Test", SerializerOptions = JsonContext6.Default.Options });
var result = await resource.ReadAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Uri = "resource://mcp/Test" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.Equal(2, result.Contents.Count);
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
index aa20a85..808ba7e 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs
@@ -62,7 +62,7 @@ public partial class McpServerToolTests
Assert.DoesNotContain("server", JsonSerializer.Serialize(tool.ProtocolTool.InputSchema, McpJsonUtilities.DefaultOptions));
var result = await tool.InvokeAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()),
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal("42", (result.Content[0] as TextContentBlock)?.Text);
}
@@ -88,7 +88,7 @@ public partial class McpServerToolTests
}, new() { Services = services });
var result = await tool.InvokeAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()),
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.NotNull(result);
Assert.NotNull(result.Content);
@@ -166,13 +166,13 @@ public partial class McpServerToolTests
Mock mockServer = new();
var ex = await Assert.ThrowsAsync(async () => await tool.InvokeAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()),
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken));
mockServer.SetupGet(s => s.Services).Returns(services);
var result = await tool.InvokeAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Services = services },
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }) { Services = services },
TestContext.Current.CancellationToken);
Assert.Equal("42", (result.Content[0] as TextContentBlock)?.Text);
}
@@ -193,7 +193,7 @@ public partial class McpServerToolTests
}, new() { Services = services });
var result = await tool.InvokeAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal("42", (result.Content[0] as TextContentBlock)?.Text);
}
@@ -208,7 +208,7 @@ public partial class McpServerToolTests
options);
var result = await tool1.InvokeAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal("""{"disposals":1}""", (result.Content[0] as TextContentBlock)?.Text);
}
@@ -223,7 +223,7 @@ public partial class McpServerToolTests
options);
var result = await tool1.InvokeAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()),
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal("""{"asyncDisposals":1}""", (result.Content[0] as TextContentBlock)?.Text);
}
@@ -242,7 +242,7 @@ public partial class McpServerToolTests
options);
var result = await tool1.InvokeAsync(
- new RequestContext(new Mock().Object, CreateTestJsonRpcRequest()) { Services = services },
+ new RequestContext(new Mock().Object, CreateTestJsonRpcRequest(), new() { Name = "" }) { Services = services },
TestContext.Current.CancellationToken);
Assert.Equal("""{"asyncDisposals":1,"disposals":0}""", (result.Content[0] as TextContentBlock)?.Text);
}
@@ -263,7 +263,7 @@ public partial class McpServerToolTests
}, new() { SerializerOptions = JsonContext2.Default.Options });
var result = await tool.InvokeAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()),
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Equal(3, result.Content.Count);
@@ -297,7 +297,7 @@ public partial class McpServerToolTests
});
var result = await tool.InvokeAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()),
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Single(result.Content);
@@ -333,7 +333,7 @@ public partial class McpServerToolTests
return (string?)null;
});
var result = await tool.InvokeAsync(
- new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()),
+ new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }),
TestContext.Current.CancellationToken);
Assert.Empty(result.Content);
}
@@ -348,7 +348,7 @@ public partial class McpServerToolTests
return "42";
});
var result = await tool.InvokeAsync(
- new RequestContext