Skip to content

Commit fb68996

Browse files
committed
Add doc with sample on accessing HttpContext
1 parent d344c65 commit fb68996

File tree

11 files changed

+372
-0
lines changed

11 files changed

+372
-0
lines changed

ModelContextProtocol.slnx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
<Folder Name="/docs/concepts/elicitation/samples/server/">
1919
<Project Path="docs/concepts/elicitation/samples/server/Elicitation.csproj" />
2020
</Folder>
21+
<Folder Name="/docs/concepts/httpcontext/" />
22+
<Folder Name="/docs/concepts/httpcontext/samples/">
23+
<Project Path="docs/concepts/httpcontext/samples/HttpContext.csproj" />
24+
</Folder>
2125
<Folder Name="/docs/concepts/logging/" />
2226
<Folder Name="/docs/concepts/logging/samples/" />
2327
<Folder Name="/docs/concepts/logging/samples/client/">
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
title: HTTP Context
3+
author: mikekistler
4+
description: How to use the logging feature in the MCP C# SDK.
5+
uid: httpcontext
6+
---
7+
8+
## HTTP Context
9+
10+
When using the Streamable HTTP transport, an MCP server may need to access the underlying [HttpContext] for a request.
11+
The [HttpContext] contains request metadata such as the HTTP headers, authorization context, and the actual path and query string for the request.
12+
13+
To access the [HttpContext], the MCP server should add the [IHttpContextAccessor] service to the application service collection (typically in Program.cs).
14+
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.
15+
Methods then use the [HttpContext property][IHttpContextAccessor.HttpContext] of the accessor to get the current context.
16+
17+
[HttpContext]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httpcontext
18+
[IHttpContextAccessor]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor
19+
[IHttpContextAccessor.HttpContext]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor.httpcontext
20+
21+
The following code snippet illustrates how to add the [IHttpContextAccessor] service to the application service collection:
22+
23+
[!code-csharp[](samples/Program.cs?name=snippet_AddHttpContextAccessor)]
24+
25+
If `ContextTools` is a class whose methods need access to the [HttpContext], the following snippet shows how it can accept
26+
an [IHttpContextAccessor] in its constructor and store it for later use:
27+
28+
[!code-csharp[](samples/Tools/ContextTools.cs?name=snippet_ConstructorParameter)]
29+
30+
A method of `ContextTools` can then access the current [HttpContext] as follows:
31+
32+
<!-- highlight the last 5 lines -->
33+
[!code-csharp[](samples/Tools/ContextTools.cs?name=snippet_AccessHttpContext)]
34+
35+
### Complete example
36+
37+
A complete example of an MCP Server that uses this method to access the [HttpContext] within tool methods is available
38+
in [this sample project](https://github.com/mikekistler/mcp-howto/tree/main/HttpContext).
39+
40+
You can run the project and then use the [TryItOut notebook](./samples/TryItOut.ipynb) to call one of the tools
41+
that access the HTTP context and returns the HTTP headers as a JSON object.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="../../../../src/ModelContextProtocol.AspNetCore/ModelContextProtocol.AspNetCore.csproj" />
11+
</ItemGroup>
12+
13+
</Project>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@HostAddress = http://localhost:3001
2+
3+
POST {{HostAddress}}/
4+
Accept: application/json, text/event-stream
5+
Content-Type: application/json
6+
MCP-Protocol-Version: 2025-06-18
7+
8+
{
9+
"jsonrpc": "2.0",
10+
"id": 2,
11+
"method": "tools/call",
12+
"params": {
13+
"name": "get_http_headers"
14+
}
15+
}
16+
17+
###
18+
19+
POST {{HostAddress}}/
20+
Accept: application/json, text/event-stream
21+
Content-Type: application/json
22+
MCP-Protocol-Version: 2025-06-18
23+
24+
{
25+
"jsonrpc": "2.0",
26+
"id": 2,
27+
"method": "tools/call",
28+
"params": {
29+
"name": "get_request_info"
30+
}
31+
}
32+
33+
###
34+
35+
POST {{HostAddress}}/
36+
Accept: application/json, text/event-stream
37+
Content-Type: application/json
38+
MCP-Protocol-Version: 2025-06-18
39+
40+
{
41+
"jsonrpc": "2.0",
42+
"id": 2,
43+
"method": "tools/call",
44+
"params": {
45+
"name": "get_user_claims"
46+
}
47+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using HttpContext.Tools;
2+
3+
var builder = WebApplication.CreateBuilder(args);
4+
5+
// Add services to the container.
6+
7+
builder.Services.AddMcpServer()
8+
.WithHttpTransport()
9+
.WithTools<ContextTools>();
10+
11+
// <snippet_AddHttpContextAccessor>
12+
builder.Services.AddHttpContextAccessor();
13+
// </snippet_AddHttpContextAccessor>
14+
15+
builder.Logging.AddConsole(options =>
16+
{
17+
options.LogToStandardErrorThreshold = LogLevel.Information;
18+
});
19+
20+
var app = builder.Build();
21+
22+
app.UseHttpsRedirection();
23+
24+
app.MapMcp();
25+
26+
app.Run();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": false,
8+
"applicationUrl": "http://localhost:3001",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
},
13+
"https": {
14+
"commandName": "Project",
15+
"dotnetRunMessages": true,
16+
"launchBrowser": false,
17+
"applicationUrl": "https://localhost:7191;http://localhost:3001",
18+
"environmentVariables": {
19+
"ASPNETCORE_ENVIRONMENT": "Development"
20+
}
21+
}
22+
}
23+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using Microsoft.AspNetCore.Http;
2+
using ModelContextProtocol.Server;
3+
using System.ComponentModel;
4+
using System.Text.Json;
5+
6+
namespace HttpContext.Tools;
7+
8+
// <snippet_ConstructorParameter>
9+
public class ContextTools
10+
{
11+
private readonly IHttpContextAccessor _httpContextAccessor;
12+
13+
public ContextTools(IHttpContextAccessor httpContextAccessor)
14+
{
15+
_httpContextAccessor = httpContextAccessor;
16+
}
17+
18+
// remainder of ContextTools follows
19+
// </snippet_ConstructorParameter>
20+
21+
// <snippet_AccessHttpContext>
22+
[McpServerTool(UseStructuredContent = true)]
23+
[Description("Retrieves the HTTP headers from the current request and returns them as a JSON object.")]
24+
public object GetHttpHeaders()
25+
{
26+
var context = _httpContextAccessor.HttpContext;
27+
if (context == null)
28+
{
29+
return "No HTTP context available";
30+
}
31+
32+
// Remainder of GetHttpHeaders method follows
33+
// </snippet_AccessHttpContext>
34+
35+
var headers = new Dictionary<string, string>();
36+
foreach (var header in context.Request.Headers)
37+
{
38+
headers[header.Key] = string.Join(", ", header.Value.ToArray());
39+
}
40+
41+
return headers;
42+
}
43+
44+
[McpServerTool(UseStructuredContent = true)]
45+
[Description("Retrieves the request information from the current HTTP context and returns it as structured content.")]
46+
public object GetRequestInfo()
47+
{
48+
var context = _httpContextAccessor.HttpContext;
49+
if (context == null)
50+
{
51+
return new { Error = "No HTTP context available" };
52+
}
53+
54+
var requestInfo = new
55+
{
56+
context.Request.Method,
57+
Path = context.Request.Path.Value,
58+
QueryString = context.Request.QueryString.Value,
59+
context.Request.ContentType,
60+
UserAgent = context.Request.Headers.UserAgent.ToString(),
61+
RemoteIpAddress = context.Connection.RemoteIpAddress?.ToString(),
62+
context.Request.IsHttps
63+
};
64+
65+
return requestInfo;
66+
}
67+
68+
[McpServerTool(UseStructuredContent = true)]
69+
[Description("Retrieves the user claims from the current HTTP context and returns them as a JSON object.")]
70+
public object GetUserClaims()
71+
{
72+
var context = _httpContextAccessor.HttpContext;
73+
if (context == null)
74+
{
75+
return "No HTTP context available";
76+
}
77+
78+
var claims = context.User.Claims.Select(c => new
79+
{
80+
c.Type,
81+
c.Value
82+
}).ToList();
83+
84+
return claims;
85+
}
86+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "37868ee2",
6+
"metadata": {
7+
"language_info": {
8+
"name": "polyglot-notebook"
9+
},
10+
"polyglot_notebook": {
11+
"kernelName": "csharp"
12+
}
13+
},
14+
"source": [
15+
"## HTTP Context\n",
16+
"\n",
17+
"This project illustrates how to access the HttpContext from tool calls. See the [README](../README.md) for more details.\n"
18+
]
19+
},
20+
{
21+
"cell_type": "markdown",
22+
"id": "093a7d4f",
23+
"metadata": {},
24+
"source": [
25+
"### Examples\n",
26+
"\n",
27+
"The following request illustrates a tool call that retrieves the HTTP headers from the [HttpContext] using the [IHttpContextAccessor].\n",
28+
"\n",
29+
"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",
30+
"which illustrate how to access other parts of the [HttpContext].\n",
31+
"\n",
32+
"```python\n",
33+
"\n",
34+
"[HttpContext]: https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httpcontext\n",
35+
"[IHttpContextAccessor]: https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor"
36+
]
37+
},
38+
{
39+
"cell_type": "code",
40+
"execution_count": 4,
41+
"id": "3324ec56",
42+
"metadata": {
43+
"language_info": {
44+
"name": "polyglot-notebook"
45+
},
46+
"polyglot_notebook": {
47+
"kernelName": "pwsh"
48+
}
49+
},
50+
"outputs": [
51+
{
52+
"name": "stdout",
53+
"output_type": "stream",
54+
"text": [
55+
"{\n",
56+
" \"result\": {\n",
57+
" \"content\": [\n",
58+
" {\n",
59+
" \"type\": \"text\",\n",
60+
" \"text\": \"[]\"\n",
61+
" }\n",
62+
" ],\n",
63+
" \"structuredContent\": {\n",
64+
" \"result\": []\n",
65+
" }\n",
66+
" },\n",
67+
" \"id\": 1,\n",
68+
" \"jsonrpc\": \"2.0\"\n",
69+
"}\n"
70+
]
71+
}
72+
],
73+
"source": [
74+
"curl -s -X POST http://localhost:3001 `\n",
75+
"-H \"ProtocolVersion: 2025-06-18\" `\n",
76+
"-H \"Accept: application/json, text/event-stream\" `\n",
77+
"-H \"Content-Type: application/json\" `\n",
78+
"-d '{\n",
79+
" \"jsonrpc\": \"2.0\",\n",
80+
" \"id\": 1,\n",
81+
" \"method\": \"tools/call\",\n",
82+
" \"params\": {\n",
83+
" \"name\": \"get_user_claims\"\n",
84+
" }\n",
85+
"}' | Where-Object { $_ -like \"data:*\" } | ForEach-Object { $_.Substring(5) } | jq"
86+
]
87+
}
88+
],
89+
"metadata": {
90+
"kernelspec": {
91+
"display_name": ".NET (C#)",
92+
"language": "C#",
93+
"name": ".net-csharp"
94+
},
95+
"language_info": {
96+
"name": "polyglot-notebook"
97+
},
98+
"polyglot_notebook": {
99+
"kernelInfo": {
100+
"defaultKernelName": "csharp",
101+
"items": [
102+
{
103+
"aliases": [],
104+
"languageName": "csharp",
105+
"name": "csharp"
106+
}
107+
]
108+
}
109+
}
110+
},
111+
"nbformat": 4,
112+
"nbformat_minor": 5
113+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*"
9+
}

0 commit comments

Comments
 (0)