You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reorder sessions.md: practical sections first, deep-dives last
Move Security up after Server configuration. Promote DI scopes to
its own top-level section. Cancellation/disposal and Advanced features
stay at the bottom.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When authentication is configured, the server automatically binds sessions to the authenticated user. This prevents one user from hijacking another user's session.
336
+
337
+
#### How it works
338
+
339
+
1. When a session is created, the server captures the authenticated user's identity from `HttpContext.User`
340
+
2. The server extracts a user ID claim in priority order:
3. On each subsequent request, the server validates that the current user matches the session's original user
345
+
4. If there's a mismatch, the server responds with `403 Forbidden`
331
346
332
-
### Service lifetimes and DI scopes
347
+
This binding is automatic — no configuration is needed. If no authentication middleware is configured, user binding is skipped (the session is not bound to any user).
348
+
## Service lifetimes and DI scopes
333
349
334
350
How the server resolves scoped services depends on the transport and session mode. The <xref:ModelContextProtocol.Server.McpServerOptions.ScopeRequests> property controls whether the server creates a new `IServiceProvider` scope for each handler invocation.
335
351
336
-
####Stateful HTTP
352
+
### Stateful HTTP
337
353
338
354
In stateful mode, the server's <xref:ModelContextProtocol.Server.McpServer.Services> is the application-level `IServiceProvider` — not a per-request scope. Because the server outlives individual HTTP requests, <xref:ModelContextProtocol.Server.McpServerOptions.ScopeRequests> defaults to `true`: each handler invocation (tool call, resource read, etc.) creates a new scope.
339
355
@@ -343,7 +359,7 @@ This means:
343
359
-**Singleton services** resolve from the application container as usual
344
360
-**Transient services** create a new instance per resolution, as usual
345
361
346
-
####Stateless HTTP
362
+
### Stateless HTTP
347
363
348
364
In stateless mode, the server uses ASP.NET Core's per-request `HttpContext.RequestServices` as its service provider, and <xref:ModelContextProtocol.Server.McpServerOptions.ScopeRequests> is automatically set to `false`. No additional scopes are created — handlers share the same HTTP request scope that middleware and other ASP.NET Core components use.
349
365
@@ -352,11 +368,11 @@ This means:
352
368
-**Scoped services** behave exactly like any other ASP.NET Core request-scoped service — middleware can set state on a scoped service and the tool handler will see it
353
369
- The DI lifetime model is identical to a standard ASP.NET Core controller or minimal API endpoint
354
370
355
-
####stdio
371
+
### stdio
356
372
357
373
The stdio transport creates a single server for the lifetime of the process. The server's <xref:ModelContextProtocol.Server.McpServer.Services> is the application-level `IServiceProvider`. By default, <xref:ModelContextProtocol.Server.McpServerOptions.ScopeRequests> is `true`, so each handler invocation gets its own scope — the same behavior as stateful HTTP.
358
374
359
-
####McpServer.Create (custom transports)
375
+
### McpServer.Create (custom transports)
360
376
361
377
When you create a server directly with <xref:ModelContextProtocol.Server.McpServer.Create*>, you control the `IServiceProvider` and transport yourself. If you pass an already-scoped provider, you can set <xref:ModelContextProtocol.Server.McpServerOptions.ScopeRequests> to `false` to avoid creating redundant nested scopes. The [InMemoryTransport sample](https://github.com/modelcontextprotocol/csharp-sdk/blob/51a4fde4d9cfa12ef9430deef7daeaac36625be8/samples/InMemoryTransport/Program.cs#L6-L14) shows a minimal example of using `McpServer.Create` with in-memory pipes:
362
378
@@ -375,15 +391,14 @@ await using McpServer server = McpServer.Create(
|**stdio**| Application services |`true` (default, configurable) | New scope per handler invocation |
385
401
|**McpServer.Create**| Caller-provided | Caller-controlled | Depends on `ScopeRequests` and whether the provider is already scoped |
386
-
387
402
## Cancellation and disposal
388
403
389
404
Every tool, prompt, and resource handler receives a `CancellationToken`. The source and behavior of that token depends on the transport and session mode. The SDK also supports the MCP [cancellation protocol](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation) for client-initiated cancellation of individual requests.
@@ -429,25 +444,6 @@ For stateless servers, shutdown is even simpler: each request is independent, so
429
444
### Stateless per-request logging
430
445
431
446
In stateless mode, each HTTP request creates and disposes a short-lived `McpServer` instance. This produces session lifecycle log entries at `Trace` level (`session created` / `session disposed`) for every request. These are typically invisible at default log levels but may appear when troubleshooting with verbose logging enabled. There is no user-facing `initialize` handshake in stateless mode — the SDK handles the per-request server lifecycle internally.
432
-
433
-
## Security
434
-
435
-
### User binding
436
-
437
-
When authentication is configured, the server automatically binds sessions to the authenticated user. This prevents one user from hijacking another user's session.
438
-
439
-
#### How it works
440
-
441
-
1. When a session is created, the server captures the authenticated user's identity from `HttpContext.User`
442
-
2. The server extracts a user ID claim in priority order:
3. On each subsequent request, the server validates that the current user matches the session's original user
447
-
4. If there's a mismatch, the server responds with `403 Forbidden`
448
-
449
-
This binding is automatic — no configuration is needed. If no authentication middleware is configured, user binding is skipped (the session is not bound to any user).
0 commit comments