Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,21 @@ public async Task<McpServerToolResultContent> InvokeToolAsync(
ThrowIfListToolsArgumentsSpecified(arguments);
}

if (headers is not null)
{
foreach (KeyValuePair<string, string> header in headers)
{
HttpHeaderValidation.ValidateNoProhibitedCharacters(
header.Key,
nameof(headers),
"HTTP header name contains invalid characters.");
HttpHeaderValidation.ValidateNoProhibitedCharacters(
header.Value,
nameof(headers),
"HTTP header value contains invalid characters.");
}
}

if (this._httpClientProvider is not null)
{
TaskCompletionSource<bool> invocationCompleted = new(TaskCreationOptions.RunContinuationsAsynchronously);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<PropertyGroup>
<InjectSharedThrow>true</InjectSharedThrow>
<InjectSharedHttpHeaderValidation>true</InjectSharedHttpHeaderValidation>
<InjectSharedOriginPinning>true</InjectSharedOriginPinning>
<InjectIsExternalInitOnLegacy>true</InjectIsExternalInitOnLegacy>
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,59 @@ public async Task DisposeAsync_WhenCalledMultipleTimes_ShouldHandleGracefullyAsy

#region HttpClientProvider Tests

[Theory]
[InlineData("\r", false, false)]
[InlineData("\n", false, false)]
[InlineData("\0", false, false)]
[InlineData("\r", true, false)]
[InlineData("\n", true, false)]
[InlineData("\0", true, false)]
[InlineData("\r", false, true)]
[InlineData("\n", false, true)]
[InlineData("\0", false, true)]
[InlineData("\r", true, true)]
[InlineData("\n", true, true)]
[InlineData("\0", true, true)]
public async Task InvokeToolAsync_WithInvalidHeader_ShouldRejectBeforeClientCreationAsync(
string invalidCharacter,
bool invalidName,
bool useProvider)
{
// Arrange
bool clientCreationAttempted = false;
Task<HttpClient?> ProviderAsync(string url, CancellationToken ct)
{
clientCreationAttempted = true;
throw new InvalidOperationException("Client creation should not be reached.");
}

HttpMessageHandler CreateHandler()
{
clientCreationAttempted = true;
throw new InvalidOperationException("Client creation should not be reached.");
}

await using DefaultMcpToolHandler handler = new(useProvider ? ProviderAsync : null, CreateHandler);
Dictionary<string, string> headers = new()
{
[invalidName ? $"X-Test{invalidCharacter}" : "X-Test"] = invalidName ? "value" : $"value{invalidCharacter}"
};

// Act
async Task actAsync() => await handler.InvokeToolAsync(
serverUrl: "https://example.com/mcp",
serverLabel: null,
toolName: "testTool",
arguments: null,
headers: headers,
connectionName: null);

// Assert
ArgumentException exception = await Assert.ThrowsAsync<ArgumentException>(actAsync);
Assert.Equal("headers", exception.ParamName);
Assert.False(clientCreationAttempted);
}

[Fact]
public async Task InvokeToolAsync_WithHttpClientProvider_ShouldCallProviderAsync()
{
Expand Down
Loading