1
+ using OpenTelemetry ;
2
+ using OpenTelemetry . Metrics ;
3
+ using OpenTelemetry . Trace ;
4
+ using AspNetCoreMcpServerPerUserTools . Tools ;
5
+ using ModelContextProtocol . Server ;
6
+
7
+ var builder = WebApplication . CreateBuilder ( args ) ;
8
+
9
+ // Register all MCP server tools - they will be filtered per user later
10
+ builder . Services . AddMcpServer ( )
11
+ . WithHttpTransport ( options =>
12
+ {
13
+ // Configure per-session options to filter tools based on user permissions
14
+ options . ConfigureSessionOptions = async ( httpContext , mcpOptions , cancellationToken ) =>
15
+ {
16
+ // Determine user role from headers (in real apps, use proper authentication)
17
+ var userRole = GetUserRole ( httpContext ) ;
18
+ var userId = GetUserId ( httpContext ) ;
19
+
20
+ // Get the tool collection that we can modify per session
21
+ var toolCollection = mcpOptions . Capabilities ? . Tools ? . ToolCollection ;
22
+ if ( toolCollection != null )
23
+ {
24
+ // Clear all tools first
25
+ toolCollection . Clear ( ) ;
26
+
27
+ // Add tools based on user role
28
+ switch ( userRole )
29
+ {
30
+ case "admin" :
31
+ // Admins get all tools
32
+ AddToolsForType < PublicTool > ( toolCollection ) ;
33
+ AddToolsForType < UserTool > ( toolCollection ) ;
34
+ AddToolsForType < AdminTool > ( toolCollection ) ;
35
+ break ;
36
+
37
+ case "user" :
38
+ // Regular users get public and user tools
39
+ AddToolsForType < PublicTool > ( toolCollection ) ;
40
+ AddToolsForType < UserTool > ( toolCollection ) ;
41
+ break ;
42
+
43
+ default :
44
+ // Anonymous/public users get only public tools
45
+ AddToolsForType < PublicTool > ( toolCollection ) ;
46
+ break ;
47
+ }
48
+ }
49
+
50
+ // Optional: Log the session configuration for debugging
51
+ var logger = httpContext . RequestServices . GetRequiredService < ILogger < Program > > ( ) ;
52
+ logger . LogInformation ( "Configured MCP session for user {UserId} with role {UserRole}, {ToolCount} tools available" ,
53
+ userId , userRole , toolCollection ? . Count ?? 0 ) ;
54
+ } ;
55
+ } )
56
+ . WithTools < PublicTool > ( )
57
+ . WithTools < UserTool > ( )
58
+ . WithTools < AdminTool > ( ) ;
59
+
60
+ // Add OpenTelemetry for observability
61
+ builder . Services . AddOpenTelemetry ( )
62
+ . WithTracing ( b => b . AddSource ( "*" )
63
+ . AddAspNetCoreInstrumentation ( )
64
+ . AddHttpClientInstrumentation ( ) )
65
+ . WithMetrics ( b => b . AddMeter ( "*" )
66
+ . AddAspNetCoreInstrumentation ( )
67
+ . AddHttpClientInstrumentation ( ) )
68
+ . WithLogging ( )
69
+ . UseOtlpExporter ( ) ;
70
+
71
+ var app = builder . Build ( ) ;
72
+
73
+ // Add middleware to log requests for demo purposes
74
+ app . Use ( async ( context , next ) =>
75
+ {
76
+ var logger = context . RequestServices . GetRequiredService < ILogger < Program > > ( ) ;
77
+ var userRole = GetUserRole ( context ) ;
78
+ var userId = GetUserId ( context ) ;
79
+
80
+ logger . LogInformation ( "Request from User {UserId} with Role {UserRole}: {Method} {Path}" ,
81
+ userId , userRole , context . Request . Method , context . Request . Path ) ;
82
+
83
+ await next ( ) ;
84
+ } ) ;
85
+
86
+ app . MapMcp ( ) ;
87
+
88
+ // Add a simple endpoint to test authentication headers
89
+ app . MapGet ( "/test-auth" , ( HttpContext context ) =>
90
+ {
91
+ var userRole = GetUserRole ( context ) ;
92
+ var userId = GetUserId ( context ) ;
93
+
94
+ return Results . Text ( $ "UserId: { userId } \n Role: { userRole } \n Message: You are authenticated as { userId } with role { userRole } ") ;
95
+ } ) ;
96
+
97
+ app . Run ( ) ;
98
+
99
+ // Helper methods for authentication - in production, use proper authentication/authorization
100
+ static string GetUserRole ( HttpContext context )
101
+ {
102
+ // Check for X-User-Role header first
103
+ if ( context . Request . Headers . TryGetValue ( "X-User-Role" , out var roleHeader ) )
104
+ {
105
+ var role = roleHeader . ToString ( ) . ToLowerInvariant ( ) ;
106
+ if ( role is "admin" or "user" or "public" )
107
+ {
108
+ return role ;
109
+ }
110
+ }
111
+
112
+ // Check for Authorization header pattern (Bearer token simulation)
113
+ if ( context . Request . Headers . TryGetValue ( "Authorization" , out var authHeader ) )
114
+ {
115
+ var auth = authHeader . ToString ( ) ;
116
+ if ( auth . StartsWith ( "Bearer admin-" , StringComparison . OrdinalIgnoreCase ) )
117
+ return "admin" ;
118
+ if ( auth . StartsWith ( "Bearer user-" , StringComparison . OrdinalIgnoreCase ) )
119
+ return "user" ;
120
+ if ( auth . StartsWith ( "Bearer " , StringComparison . OrdinalIgnoreCase ) )
121
+ return "public" ;
122
+ }
123
+
124
+ // Default to public access
125
+ return "public" ;
126
+ }
127
+
128
+ static string GetUserId ( HttpContext context )
129
+ {
130
+ // Check for X-User-Id header first
131
+ if ( context . Request . Headers . TryGetValue ( "X-User-Id" , out var userIdHeader ) )
132
+ {
133
+ return userIdHeader . ToString ( ) ;
134
+ }
135
+
136
+ // Extract from Authorization header if present
137
+ if ( context . Request . Headers . TryGetValue ( "Authorization" , out var authHeader ) )
138
+ {
139
+ var auth = authHeader . ToString ( ) ;
140
+ if ( auth . StartsWith ( "Bearer " , StringComparison . OrdinalIgnoreCase ) )
141
+ {
142
+ var token = auth [ "Bearer " . Length ..] ;
143
+ return token . Contains ( '-' ) ? token : $ "user-{ token } ";
144
+ }
145
+ }
146
+
147
+ // Generate anonymous ID
148
+ return $ "anonymous-{ Guid . NewGuid ( ) : N} "[ ..16 ] ;
149
+ }
150
+
151
+ static void AddToolsForType < [ System . Diagnostics . CodeAnalysis . DynamicallyAccessedMembers (
152
+ System . Diagnostics . CodeAnalysis . DynamicallyAccessedMemberTypes . PublicMethods ) ] T > (
153
+ McpServerPrimitiveCollection < McpServerTool > toolCollection )
154
+ {
155
+ var toolType = typeof ( T ) ;
156
+ var methods = toolType . GetMethods ( System . Reflection . BindingFlags . Public | System . Reflection . BindingFlags . Static )
157
+ . Where ( m => m . GetCustomAttributes ( typeof ( McpServerToolAttribute ) , false ) . Any ( ) ) ;
158
+
159
+ foreach ( var method in methods )
160
+ {
161
+ try
162
+ {
163
+ var tool = McpServerTool . Create ( method , target : null , new McpServerToolCreateOptions ( ) ) ;
164
+ toolCollection . Add ( tool ) ;
165
+ }
166
+ catch ( Exception ex )
167
+ {
168
+ // Log error but continue with other tools
169
+ Console . WriteLine ( $ "Failed to add tool { toolType . Name } .{ method . Name } : { ex . Message } ") ;
170
+ }
171
+ }
172
+ }
0 commit comments