diff --git a/ModelContextProtocol.slnx b/ModelContextProtocol.slnx index 30860cfe3..d5588fdf5 100644 --- a/ModelContextProtocol.slnx +++ b/ModelContextProtocol.slnx @@ -18,6 +18,10 @@ + + + + diff --git a/docs/concepts/elicitation/samples/client/ElicitationClient.csproj b/docs/concepts/elicitation/samples/client/ElicitationClient.csproj index 312628b65..4be0d6ec7 100644 --- a/docs/concepts/elicitation/samples/client/ElicitationClient.csproj +++ b/docs/concepts/elicitation/samples/client/ElicitationClient.csproj @@ -5,6 +5,8 @@ net9.0 enable enable + false + false diff --git a/docs/concepts/elicitation/samples/server/Elicitation.csproj b/docs/concepts/elicitation/samples/server/Elicitation.csproj index b813fc123..f4998aa12 100644 --- a/docs/concepts/elicitation/samples/server/Elicitation.csproj +++ b/docs/concepts/elicitation/samples/server/Elicitation.csproj @@ -4,6 +4,8 @@ net9.0 enable enable + false + false diff --git a/docs/concepts/httpcontext/httpcontext.md b/docs/concepts/httpcontext/httpcontext.md new file mode 100644 index 000000000..51bbd050a --- /dev/null +++ b/docs/concepts/httpcontext/httpcontext.md @@ -0,0 +1,31 @@ +--- +title: HTTP Context +author: mikekistler +description: How to access the HttpContext in the MCP C# SDK. +uid: httpcontext +--- + +## HTTP Context + +When using the Streamable HTTP transport, an MCP server may need to access the underlying [HttpContext] for a request. +The [HttpContext] contains request metadata such as the HTTP headers, authorization context, and the actual path and query string for the request. + +To access the [HttpContext], the MCP server should add the [IHttpContextAccessor] service to the application service collection (typically in Program.cs). +Then any classes, e.g. a class containing MCP tools, should accept an [IHttpContextAccessor] in their constructor and store this for use by its methods. +Methods then use the [HttpContext property][IHttpContextAccessor.HttpContext] of the accessor to get the current context. + +[HttpContext]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httpcontext +[IHttpContextAccessor]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor +[IHttpContextAccessor.HttpContext]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor.httpcontext + +The following code snippet illustrates how to add the [IHttpContextAccessor] service to the application service collection: + +[!code-csharp[](samples/Program.cs?name=snippet_AddHttpContextAccessor)] + +Any class that needs access to the [HttpContext] can accept an [IHttpContextAccessor] in its constructor and store it for later use. +Methods of the class can then access the current [HttpContext] using the stored accessor. + +The following code snippet shows the `ContextTools` class accepting an [IHttpContextAccessor] in its primary constructor +and the `GetHttpHeaders` method accessing the current [HttpContext] to retrieve the HTTP headers from the current request. + +[!code-csharp[](samples/Tools/ContextTools.cs?name=snippet_AccessHttpContext)] diff --git a/docs/concepts/httpcontext/samples/HttpContext.csproj b/docs/concepts/httpcontext/samples/HttpContext.csproj new file mode 100644 index 000000000..2982d8f87 --- /dev/null +++ b/docs/concepts/httpcontext/samples/HttpContext.csproj @@ -0,0 +1,18 @@ + + + + net9.0 + enable + enable + + + + false + false + + + + + + + diff --git a/docs/concepts/httpcontext/samples/HttpContext.http b/docs/concepts/httpcontext/samples/HttpContext.http new file mode 100644 index 000000000..838457e9b --- /dev/null +++ b/docs/concepts/httpcontext/samples/HttpContext.http @@ -0,0 +1,15 @@ +@HostAddress = http://localhost:3001 + +POST {{HostAddress}}/ +Accept: application/json, text/event-stream +Content-Type: application/json +MCP-Protocol-Version: 2025-06-18 + +{ + "jsonrpc": "2.0", + "id": 2, + "method": "tools/call", + "params": { + "name": "get_http_headers" + } +} diff --git a/docs/concepts/httpcontext/samples/Program.cs b/docs/concepts/httpcontext/samples/Program.cs new file mode 100644 index 000000000..043e6069d --- /dev/null +++ b/docs/concepts/httpcontext/samples/Program.cs @@ -0,0 +1,26 @@ +using HttpContext.Tools; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddMcpServer() + .WithHttpTransport() + .WithTools(); + +// +builder.Services.AddHttpContextAccessor(); +// + +builder.Logging.AddConsole(options => +{ + options.LogToStandardErrorThreshold = LogLevel.Information; +}); + +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.MapMcp(); + +app.Run(); diff --git a/docs/concepts/httpcontext/samples/Properties/launchSettings.json b/docs/concepts/httpcontext/samples/Properties/launchSettings.json new file mode 100644 index 000000000..c6eb0fa56 --- /dev/null +++ b/docs/concepts/httpcontext/samples/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:3001", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7191;http://localhost:3001", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/concepts/httpcontext/samples/Tools/ContextTools.cs b/docs/concepts/httpcontext/samples/Tools/ContextTools.cs new file mode 100644 index 000000000..222130bb9 --- /dev/null +++ b/docs/concepts/httpcontext/samples/Tools/ContextTools.cs @@ -0,0 +1,28 @@ +using ModelContextProtocol.Server; +using System.ComponentModel; + +namespace HttpContext.Tools; + +// +public class ContextTools(IHttpContextAccessor httpContextAccessor) +{ + [McpServerTool(UseStructuredContent = true)] + [Description("Retrieves the HTTP headers from the current request and returns them as a JSON object.")] + public object GetHttpHeaders() + { + var context = httpContextAccessor.HttpContext; + if (context == null) + { + return "No HTTP context available"; + } + + var headers = new Dictionary(); + foreach (var header in context.Request.Headers) + { + headers[header.Key] = string.Join(", ", header.Value.ToArray()); + } + + return headers; + } +// +} diff --git a/docs/concepts/httpcontext/samples/TryItOut.ipynb b/docs/concepts/httpcontext/samples/TryItOut.ipynb new file mode 100644 index 000000000..95ebfafec --- /dev/null +++ b/docs/concepts/httpcontext/samples/TryItOut.ipynb @@ -0,0 +1,112 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "37868ee2", + "metadata": { + "language_info": { + "name": "polyglot-notebook" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "## HTTP Context\n", + "\n", + "This project illustrates how to access the HttpContext from tool calls. See the [README](../README.md) for more details.\n" + ] + }, + { + "cell_type": "markdown", + "id": "093a7d4f", + "metadata": {}, + "source": [ + "### Examples\n", + "\n", + "The following request illustrates a tool call that retrieves the HTTP headers from the [HttpContext] using the [IHttpContextAccessor].\n", + "\n", + "The server implements two other tools, `get_request_info` and `get_user_claims`. You can modify the code below to call these tools as well,\n", + "which illustrate how to access other parts of the [HttpContext].\n", + "\n", + "\n", + "[HttpContext]: https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httpcontext\n", + "[IHttpContextAccessor]: https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3324ec56", + "metadata": { + "language_info": { + "name": "polyglot-notebook" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"result\": {\n", + " \"content\": [\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": \"[]\"\n", + " }\n", + " ],\n", + " \"structuredContent\": {\n", + " \"result\": []\n", + " }\n", + " },\n", + " \"id\": 1,\n", + " \"jsonrpc\": \"2.0\"\n", + "}\n" + ] + } + ], + "source": [ + "curl -s -X POST http://localhost:3001 `\n", + "-H \"ProtocolVersion: 2025-06-18\" `\n", + "-H \"Accept: application/json, text/event-stream\" `\n", + "-H \"Content-Type: application/json\" `\n", + "-d '{\n", + " \"jsonrpc\": \"2.0\",\n", + " \"id\": 1,\n", + " \"method\": \"tools/call\",\n", + " \"params\": {\n", + " \"name\": \"get_user_claims\"\n", + " }\n", + "}' | Where-Object { $_ -like \"data:*\" } | ForEach-Object { $_.Substring(5) } | jq" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "polyglot-notebook" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "languageName": "csharp", + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/concepts/httpcontext/samples/appsettings.Development.json b/docs/concepts/httpcontext/samples/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/docs/concepts/httpcontext/samples/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/concepts/httpcontext/samples/appsettings.json b/docs/concepts/httpcontext/samples/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/docs/concepts/httpcontext/samples/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/concepts/logging/samples/client/LoggingClient.csproj b/docs/concepts/logging/samples/client/LoggingClient.csproj index 6ed1a9542..9f020005d 100644 --- a/docs/concepts/logging/samples/client/LoggingClient.csproj +++ b/docs/concepts/logging/samples/client/LoggingClient.csproj @@ -5,6 +5,8 @@ net9.0 enable enable + false + false diff --git a/docs/concepts/logging/samples/server/Logging.csproj b/docs/concepts/logging/samples/server/Logging.csproj index b813fc123..f4998aa12 100644 --- a/docs/concepts/logging/samples/server/Logging.csproj +++ b/docs/concepts/logging/samples/server/Logging.csproj @@ -4,6 +4,8 @@ net9.0 enable enable + false + false diff --git a/docs/concepts/progress/samples/client/ProgressClient.csproj b/docs/concepts/progress/samples/client/ProgressClient.csproj index 6ed1a9542..9f020005d 100644 --- a/docs/concepts/progress/samples/client/ProgressClient.csproj +++ b/docs/concepts/progress/samples/client/ProgressClient.csproj @@ -5,6 +5,8 @@ net9.0 enable enable + false + false diff --git a/docs/concepts/progress/samples/server/Progress.csproj b/docs/concepts/progress/samples/server/Progress.csproj index b813fc123..f4998aa12 100644 --- a/docs/concepts/progress/samples/server/Progress.csproj +++ b/docs/concepts/progress/samples/server/Progress.csproj @@ -4,6 +4,8 @@ net9.0 enable enable + false + false diff --git a/docs/concepts/toc.yml b/docs/concepts/toc.yml index 2f7c930fa..97ffbd16e 100644 --- a/docs/concepts/toc.yml +++ b/docs/concepts/toc.yml @@ -13,7 +13,7 @@ items: items: - name: Logging uid: logging -- name: Server Features - items: + - name: HTTP Context + uid: httpcontext - name: Filters uid: filters \ No newline at end of file