fa-mcp-sdk 0.2.249 → 0.2.258

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/cli-template/FA-MCP-SDK-DOC/00-FA-MCP-SDK-index.md +45 -161
  2. package/cli-template/FA-MCP-SDK-DOC/01-getting-started.md +71 -226
  3. package/cli-template/FA-MCP-SDK-DOC/02-1-tools-and-api.md +80 -360
  4. package/cli-template/FA-MCP-SDK-DOC/02-2-prompts-and-resources.md +191 -342
  5. package/cli-template/FA-MCP-SDK-DOC/03-configuration.md +141 -279
  6. package/cli-template/FA-MCP-SDK-DOC/04-authentication.md +73 -522
  7. package/cli-template/FA-MCP-SDK-DOC/05-ad-authorization.md +68 -419
  8. package/cli-template/FA-MCP-SDK-DOC/06-utilities.md +64 -447
  9. package/cli-template/FA-MCP-SDK-DOC/07-testing-and-operations.md +39 -196
  10. package/cli-template/package.json +2 -1
  11. package/config/local.yaml +1 -1
  12. package/dist/core/_types_/types.d.ts +36 -10
  13. package/dist/core/_types_/types.d.ts.map +1 -1
  14. package/dist/core/auth/admin-auth.js +1 -1
  15. package/dist/core/auth/admin-auth.js.map +1 -1
  16. package/dist/core/auth/middleware.js +8 -8
  17. package/dist/core/auth/middleware.js.map +1 -1
  18. package/dist/core/auth/multi-auth.d.ts.map +1 -1
  19. package/dist/core/auth/multi-auth.js +15 -14
  20. package/dist/core/auth/multi-auth.js.map +1 -1
  21. package/dist/core/index.d.ts +1 -1
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/core/index.js.map +1 -1
  24. package/dist/core/mcp/create-mcp-server.js +8 -9
  25. package/dist/core/mcp/create-mcp-server.js.map +1 -1
  26. package/dist/core/mcp/prompts.d.ts +10 -5
  27. package/dist/core/mcp/prompts.d.ts.map +1 -1
  28. package/dist/core/mcp/prompts.js +17 -15
  29. package/dist/core/mcp/prompts.js.map +1 -1
  30. package/dist/core/mcp/resources.d.ts +4 -4
  31. package/dist/core/mcp/resources.d.ts.map +1 -1
  32. package/dist/core/mcp/resources.js +21 -20
  33. package/dist/core/mcp/resources.js.map +1 -1
  34. package/dist/core/utils/utils.d.ts +2 -1
  35. package/dist/core/utils/utils.d.ts.map +1 -1
  36. package/dist/core/utils/utils.js +2 -2
  37. package/dist/core/utils/utils.js.map +1 -1
  38. package/dist/core/web/home-api.d.ts.map +1 -1
  39. package/dist/core/web/home-api.js +4 -3
  40. package/dist/core/web/home-api.js.map +1 -1
  41. package/dist/core/web/server-http.d.ts.map +1 -1
  42. package/dist/core/web/server-http.js +36 -21
  43. package/dist/core/web/server-http.js.map +1 -1
  44. package/package.json +1 -1
  45. package/scripts/update-doc.js +21 -0
  46. package/src/template/start.ts +1 -1
@@ -1,528 +1,177 @@
1
- # Advanced AD Group Authorization
1
+ # AD Group Authorization
2
2
 
3
- This document demonstrates how to implement additional authorization based on Active Directory (AD)
4
- group membership. These examples assume JWT token authentication (`jwtToken`) is configured,
5
- and the user information is extracted from the JWT payload.
3
+ Authorization by AD group membership. Assumes JWT auth is configured.
6
4
 
7
5
  ## AD Configuration Types
8
6
 
9
- ### `IADConfig`
10
-
11
- Main Active Directory configuration interface for group membership checks and NTLM authentication.
12
-
13
7
  ```typescript
14
- import { IADConfig } from 'fa-mcp-sdk';
15
-
16
- // Type Definition:
17
8
  interface IADConfig {
18
9
  ad: {
19
- domains: {
20
- // Key is domain name (e.g., 'OFFICE', 'CORP')
21
- [domainName: string]: IDcConfig;
22
- };
23
- tlsOptions?: ConnectionOptions; // TLS options for LDAPS connections
24
- strategy?: EAuthStrategy; // NTLM authentication strategy
25
- groupCacheTtlMs?: number; // Cache TTL for group checks (default: 10 minutes)
26
- dnCacheTtlMs?: number; // Cache TTL for DN lookups (default: 24 hours)
10
+ domains: { [domainName: string]: IDcConfig };
11
+ tlsOptions?: ConnectionOptions;
12
+ groupCacheTtlMs?: number; // Default: 10 min
13
+ dnCacheTtlMs?: number; // Default: 24 hours
27
14
  };
28
15
  }
29
- ```
30
-
31
- ### `IDcConfig`
32
16
 
33
- Domain Controller configuration for individual AD domains.
34
-
35
- ```typescript
36
- import { IDcConfig } from 'fa-mcp-sdk';
37
-
38
- // Type Definition:
39
17
  interface IDcConfig {
40
- controllers: string[]; // LDAP URLs: ['ldap://dc1.corp.com', 'ldap://dc2.corp.com']
41
- username: string; // Service account for LDAP queries
42
- password: string; // Service account password
43
- baseDn?: string; // Base DN for searches (auto-derived from URL if not set)
44
- default?: boolean; // Mark as default domain for operations
45
-
46
- // Internal fields (assigned during config processing):
47
- name?: string; // Domain name
48
- hostReSource?: string; // RegExp source for hostname matching
49
- hostRe?: RegExp; // Compiled hostname matcher
18
+ controllers: string[]; // ['ldap://dc1.corp.com']
19
+ username: string;
20
+ password: string;
21
+ baseDn?: string; // Auto-derived if omitted
22
+ default?: boolean;
50
23
  }
51
24
  ```
52
25
 
53
- **Configuration Example (`config/default.yaml`):**
54
-
55
26
  ```yaml
27
+ # config/default.yaml
56
28
  ad:
57
- groupCacheTtlMs: 600000 # 10 minutes
58
- dnCacheTtlMs: 86400000 # 24 hours
29
+ groupCacheTtlMs: 600000
59
30
  domains:
60
31
  CORP:
61
32
  default: true
62
- controllers:
63
- - 'ldap://dc1.corp.com'
64
- - 'ldap://dc2.corp.com'
33
+ controllers: ['ldap://dc1.corp.com']
65
34
  username: 'svc_mcp@corp.com'
66
- password: '${AD_SERVICE_PASSWORD}' # From environment variable
67
- baseDn: 'DC=corp,DC=com' # Optional, auto-derived if omitted
68
- PARTNER:
69
- controllers:
70
- - 'ldap://dc1.partner.local'
71
- username: 'svc_reader@partner.local'
72
- password: '${AD_PARTNER_PASSWORD}'
35
+ password: '${AD_SERVICE_PASSWORD}'
73
36
  ```
74
37
 
75
- ---
76
-
77
- ## Configuration for AD Group Authorization
38
+ ## Custom Config Extension
78
39
 
79
- First, extend your configuration to include the required AD group:
80
-
81
- **`src/_types_/custom-config.ts`:**
82
40
  ```typescript
41
+ // src/_types_/custom-config.ts
83
42
  import { AppConfig } from 'fa-mcp-sdk';
84
43
 
85
- export interface IGroupAccessConfig {
86
- groupAccess: {
87
- /** AD group required for access */
88
- requiredGroup: string;
89
- /** Bypass group check for debugging (default: false) */
90
- bypassGroupCheck?: boolean;
91
- /** Cache TTL in seconds (default: 300) */
92
- cacheTtlSeconds?: number;
93
- };
44
+ interface IGroupAccessConfig {
45
+ groupAccess: { requiredGroup: string; bypassGroupCheck?: boolean };
94
46
  }
95
-
96
47
  export interface CustomAppConfig extends AppConfig, IGroupAccessConfig {}
97
48
  ```
98
49
 
99
- **`config/default.yaml`:**
100
50
  ```yaml
51
+ # config/default.yaml
101
52
  groupAccess:
102
53
  requiredGroup: "DOMAIN\\MCP-Users"
103
54
  bypassGroupCheck: false
104
- cacheTtlSeconds: 300
105
55
  ```
106
56
 
107
- ---
108
-
109
- ## Example 1: HTTP Server Level Access Restriction
57
+ ## Example 1: HTTP Level Restriction
110
58
 
111
- This example uses `customAuthValidator` to check AD group membership at the HTTP server level.
112
- If the user is not in the required group, a 403 Forbidden error is returned before any
113
- MCP request processing.
59
+ Block unauthorized users at HTTP level (403 before MCP processing):
114
60
 
115
- **`src/start.ts`:**
116
61
  ```typescript
117
- import {
118
- appConfig,
119
- initMcpServer,
120
- McpServerData,
121
- CustomAuthValidator,
122
- AuthResult,
123
- initADGroupChecker,
124
- } from 'fa-mcp-sdk';
125
- // Note: JWT validation is handled automatically by the SDK's multi-auth system.
126
- // The customAuthValidator receives parsed JWT payload when jwtToken auth is enabled.
127
- import { tools } from './tools/tools.js';
128
- import { handleToolCall } from './tools/handle-tool-call.js';
129
- import { AGENT_BRIEF } from './prompts/agent-brief.js';
130
- import { AGENT_PROMPT } from './prompts/agent-prompt.js';
62
+ // src/start.ts
63
+ import { appConfig, initMcpServer, CustomAuthValidator, initADGroupChecker } from 'fa-mcp-sdk';
131
64
  import { CustomAppConfig } from './_types_/custom-config.js';
132
65
 
133
- // Get typed config
134
66
  const config = appConfig as CustomAppConfig;
135
-
136
- // Initialize AD group checker
137
67
  const { isUserInGroup } = initADGroupChecker();
138
68
 
139
- /**
140
- * Custom authentication validator with AD group membership check
141
- * This validator is called AFTER the SDK's standard JWT validation.
142
- * The req object contains parsed authInfo from the standard auth flow.
143
- * Returns 403 Forbidden if user is not in the required AD group
144
- */
145
- const customAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
146
- // Extract user info from request (populated by standard auth)
69
+ const customAuthValidator: CustomAuthValidator = async (req) => {
147
70
  const authInfo = (req as any).authInfo;
71
+ if (!authInfo?.username) return { success: false, error: 'User info unavailable' };
148
72
 
149
- // If standard auth failed or no user info, let standard auth handle it
150
- if (!authInfo?.username) {
151
- return { success: false, error: 'User information not available' };
152
- }
153
-
154
- const username = authInfo.username;
155
-
156
- // Bypass group check if configured (for debugging)
157
73
  if (config.groupAccess.bypassGroupCheck) {
158
- return {
159
- success: true,
160
- authType: authInfo.authType,
161
- username,
162
- payload: authInfo.payload,
163
- };
74
+ return { success: true, authType: authInfo.authType, username: authInfo.username };
164
75
  }
165
76
 
166
- // Check AD group membership
167
- const requiredGroup = config.groupAccess.requiredGroup;
168
- try {
169
- const isInGroup = await isUserInGroup(username, requiredGroup);
170
-
171
- if (!isInGroup) {
172
- return {
173
- success: false,
174
- error: `Forbidden: User '${username}' is not a member of group '${requiredGroup}'`,
175
- };
176
- }
177
-
178
- return {
179
- success: true,
180
- authType: authInfo.authType,
181
- username,
182
- payload: authInfo.payload,
183
- };
184
- } catch (error) {
185
- const errorMessage = error instanceof Error ? error.message : String(error);
186
- return {
187
- success: false,
188
- error: `AD group check failed: ${errorMessage}`,
189
- };
77
+ const isInGroup = await isUserInGroup(authInfo.username, config.groupAccess.requiredGroup);
78
+ if (!isInGroup) {
79
+ return { success: false, error: `Forbidden: Not in group '${config.groupAccess.requiredGroup}'` };
190
80
  }
191
- };
192
81
 
193
- const startProject = async (): Promise<void> => {
194
- const serverData: McpServerData = {
195
- tools,
196
- toolHandler: handleToolCall,
197
- agentBrief: AGENT_BRIEF,
198
- agentPrompt: AGENT_PROMPT,
199
-
200
- // Enable custom authentication with AD group check
201
- customAuthValidator,
202
-
203
- // ... other configuration
204
- };
205
-
206
- await initMcpServer(serverData);
82
+ return { success: true, authType: authInfo.authType, username: authInfo.username };
207
83
  };
208
84
 
209
- startProject().catch(console.error);
85
+ await initMcpServer({ ..., customAuthValidator });
210
86
  ```
211
87
 
212
- **Result**: If the user is not in the required AD group, they receive HTTP 403 Forbidden
213
- response before any MCP processing occurs.
214
-
215
- ---
88
+ ## Example 2: All Tools Restriction
216
89
 
217
- ## Example 2: Access Restriction to ALL MCP Tools
90
+ Check in `toolHandler` (MCP error response):
218
91
 
219
- This example restricts access to all MCP tools by checking AD group membership in the
220
- `toolHandler` function. If the user is not in the required group, the tool call returns
221
- an MCP error with "Forbidden" message.
222
-
223
- **`src/tools/handle-tool-call.ts`:**
224
92
  ```typescript
225
- import {
226
- formatToolResult,
227
- ToolExecutionError,
228
- logger,
229
- appConfig,
230
- initADGroupChecker,
231
- IToolHandlerParams,
232
- } from 'fa-mcp-sdk';
93
+ // src/tools/handle-tool-call.ts
94
+ import { ToolExecutionError, appConfig, initADGroupChecker, IToolHandlerParams } from 'fa-mcp-sdk';
233
95
  import { CustomAppConfig } from '../_types_/custom-config.js';
234
96
 
235
- // Get typed config
236
97
  const config = appConfig as CustomAppConfig;
237
-
238
- // Initialize AD group checker
239
98
  const { isUserInGroup } = initADGroupChecker();
240
99
 
241
- /**
242
- * Check if user has access to MCP tools based on AD group membership
243
- */
244
- async function checkToolAccess(payload: IToolHandlerParams['payload']): Promise<void> {
245
- // Skip check if bypass is enabled
246
- if (config.groupAccess.bypassGroupCheck) {
247
- return;
248
- }
100
+ async function checkToolAccess(payload: IToolHandlerParams['payload']) {
101
+ if (config.groupAccess.bypassGroupCheck) return;
102
+ if (!payload?.user) throw new ToolExecutionError('auth', 'User info unavailable');
249
103
 
250
- if (!payload?.user) {
251
- throw new ToolExecutionError('authorization', 'Forbidden: User information not available');
252
- }
253
-
254
- const username = payload.user;
255
- const requiredGroup = config.groupAccess.requiredGroup;
256
-
257
- try {
258
- const isInGroup = await isUserInGroup(username, requiredGroup);
259
-
260
- if (!isInGroup) {
261
- throw new ToolExecutionError(
262
- 'authorization',
263
- `Forbidden: User '${username}' is not authorized to use MCP tools. ` +
264
- `Required group: '${requiredGroup}'`
265
- );
266
- }
267
- } catch (error) {
268
- if (error instanceof ToolExecutionError) {
269
- throw error;
270
- }
271
- const errorMessage = error instanceof Error ? error.message : String(error);
272
- throw new ToolExecutionError('authorization', `Forbidden: AD group check failed - ${errorMessage}`);
104
+ const isInGroup = await isUserInGroup(payload.user, config.groupAccess.requiredGroup);
105
+ if (!isInGroup) {
106
+ throw new ToolExecutionError('auth', `Forbidden: User not in '${config.groupAccess.requiredGroup}'`);
273
107
  }
274
108
  }
275
109
 
276
- export const handleToolCall = async (params: IToolHandlerParams): Promise<any> => {
277
- const { name, arguments: args, headers, payload } = params;
278
-
279
- logger.info(`Tool called: ${name} by user: ${payload?.user || 'unknown'}`);
280
-
281
- // Check AD group membership for ALL tools
282
- await checkToolAccess(payload);
283
-
284
- try {
285
- switch (name) {
286
- case 'my_tool':
287
- return await handleMyTool(args);
288
- case 'another_tool':
289
- return await handleAnotherTool(args);
290
- default:
291
- throw new ToolExecutionError(name, `Unknown tool: ${name}`);
292
- }
293
- } catch (error) {
294
- logger.error(`Tool execution failed for ${name}:`, error);
295
- throw error;
296
- }
110
+ export const handleToolCall = async (params: IToolHandlerParams) => {
111
+ await checkToolAccess(params.payload); // Check ALL tools
112
+ // ... tool switch logic
297
113
  };
298
-
299
- async function handleMyTool(args: any): Promise<any> {
300
- // Tool implementation
301
- return formatToolResult({ message: 'Tool executed successfully', args });
302
- }
303
-
304
- async function handleAnotherTool(args: any): Promise<any> {
305
- // Tool implementation
306
- return formatToolResult({ message: 'Another tool executed', args });
307
- }
308
- ```
309
-
310
- **Result**: If the user is not in the required AD group, any tool call returns an MCP error:
311
- ```json
312
- {
313
- "jsonrpc": "2.0",
314
- "error": {
315
- "code": -32603,
316
- "message": "Forbidden: User 'john.doe' is not authorized to use MCP tools. Required group: 'DOMAIN\\MCP-Users'"
317
- },
318
- "id": 1
319
- }
320
114
  ```
321
115
 
322
- ---
116
+ ## Example 3: Per-Tool Restriction
323
117
 
324
- ## Example 3: Access Restriction to a SPECIFIC MCP Tool
118
+ Different groups for different tools:
325
119
 
326
- This example restricts access to specific MCP tools based on AD group membership.
327
- Different tools can require different AD groups.
328
-
329
- **`src/_types_/custom-config.ts`:**
330
120
  ```typescript
331
- import { AppConfig } from 'fa-mcp-sdk';
332
-
333
- export interface IToolGroupAccessConfig {
121
+ // src/_types_/custom-config.ts
122
+ interface IToolGroupAccessConfig {
334
123
  toolGroupAccess: {
335
- /** Default group required for tools without specific configuration */
336
124
  defaultGroup?: string;
337
- /** Specific group requirements per tool */
338
- tools: Record<string, {
339
- /** AD group required for this tool */
340
- requiredGroup: string;
341
- /** Allow access without group check (default: false) */
342
- public?: boolean;
343
- }>;
344
- /** Bypass all group checks (for debugging) */
125
+ tools: Record<string, { requiredGroup?: string; public?: boolean }>;
345
126
  bypassGroupCheck?: boolean;
346
127
  };
347
128
  }
348
-
349
- export interface CustomAppConfig extends AppConfig, IToolGroupAccessConfig {}
350
129
  ```
351
130
 
352
- **`config/default.yaml`:**
353
131
  ```yaml
132
+ # config/default.yaml
354
133
  toolGroupAccess:
355
134
  defaultGroup: "DOMAIN\\MCP-Users"
356
135
  bypassGroupCheck: false
357
136
  tools:
358
137
  get_public_data:
359
- public: true # No group check required
138
+ public: true
360
139
  get_user_data:
361
140
  requiredGroup: "DOMAIN\\MCP-Users"
362
- modify_data:
363
- requiredGroup: "DOMAIN\\MCP-DataModifiers"
364
141
  admin_operation:
365
142
  requiredGroup: "DOMAIN\\MCP-Admins"
366
143
  ```
367
144
 
368
- **`src/tools/handle-tool-call.ts`:**
369
145
  ```typescript
370
- import {
371
- formatToolResult,
372
- ToolExecutionError,
373
- logger,
374
- appConfig,
375
- initADGroupChecker,
376
- IToolHandlerParams,
377
- } from 'fa-mcp-sdk';
378
- import { CustomAppConfig } from '../_types_/custom-config.js';
379
-
380
- // Get typed config
381
- const config = appConfig as CustomAppConfig;
382
-
383
- // Initialize AD group checker
384
- const { isUserInGroup } = initADGroupChecker();
385
-
386
- /**
387
- * Check if user has access to a specific tool based on AD group membership
388
- */
389
- async function checkToolAccess(
390
- toolName: string,
391
- payload: IToolHandlerParams['payload']
392
- ): Promise<void> {
146
+ // src/tools/handle-tool-call.ts
147
+ async function checkToolAccess(toolName: string, payload: IToolHandlerParams['payload']) {
393
148
  const toolAccess = config.toolGroupAccess;
394
-
395
- // Skip check if bypass is enabled
396
- if (toolAccess.bypassGroupCheck) {
397
- return;
398
- }
149
+ if (toolAccess.bypassGroupCheck) return;
399
150
 
400
151
  const toolConfig = toolAccess.tools[toolName];
152
+ if (toolConfig?.public) return;
401
153
 
402
- // If tool is marked as public, allow access
403
- if (toolConfig?.public) {
404
- return;
405
- }
406
-
407
- // Check user availability
408
- if (!payload?.user) {
409
- throw new ToolExecutionError(
410
- toolName,
411
- `Forbidden: User information not available for tool '${toolName}'`
412
- );
413
- }
154
+ if (!payload?.user) throw new ToolExecutionError(toolName, 'User info unavailable');
414
155
 
415
- const username = payload.user;
416
-
417
- // Determine required group: tool-specific or default
418
156
  const requiredGroup = toolConfig?.requiredGroup || toolAccess.defaultGroup;
157
+ if (!requiredGroup) return;
419
158
 
420
- if (!requiredGroup) {
421
- // No group configured - allow access
422
- return;
423
- }
424
-
425
- try {
426
- const isInGroup = await isUserInGroup(username, requiredGroup);
427
-
428
- if (!isInGroup) {
429
- throw new ToolExecutionError(
430
- toolName,
431
- `Forbidden: User '${username}' is not authorized to use tool '${toolName}'. ` +
432
- `Required group: '${requiredGroup}'`
433
- );
434
- }
435
-
436
- logger.info(`User '${username}' authorized for tool '${toolName}' via group '${requiredGroup}'`);
437
- } catch (error) {
438
- if (error instanceof ToolExecutionError) {
439
- throw error;
440
- }
441
- const errorMessage = error instanceof Error ? error.message : String(error);
442
- throw new ToolExecutionError(
443
- toolName,
444
- `Forbidden: AD group check failed for tool '${toolName}' - ${errorMessage}`
445
- );
159
+ const isInGroup = await isUserInGroup(payload.user, requiredGroup);
160
+ if (!isInGroup) {
161
+ throw new ToolExecutionError(toolName, `Forbidden: User not in '${requiredGroup}'`);
446
162
  }
447
163
  }
448
164
 
449
- export const handleToolCall = async (params: IToolHandlerParams): Promise<any> => {
450
- const { name, arguments: args, headers, payload } = params;
451
-
452
- logger.info(`Tool called: ${name} by user: ${payload?.user || 'unknown'}`);
453
-
454
- // Check AD group membership for the specific tool
455
- await checkToolAccess(name, payload);
456
-
457
- try {
458
- switch (name) {
459
- case 'get_public_data':
460
- // Public tool - no group check was performed
461
- return await handleGetPublicData(args);
462
-
463
- case 'get_user_data':
464
- // Requires MCP-Users group
465
- return await handleGetUserData(args);
466
-
467
- case 'modify_data':
468
- // Requires MCP-DataModifiers group
469
- return await handleModifyData(args);
470
-
471
- case 'admin_operation':
472
- // Requires MCP-Admins group
473
- return await handleAdminOperation(args);
474
-
475
- default:
476
- // Unknown tools use defaultGroup if configured
477
- throw new ToolExecutionError(name, `Unknown tool: ${name}`);
478
- }
479
- } catch (error) {
480
- logger.error(`Tool execution failed for ${name}:`, error);
481
- throw error;
482
- }
165
+ export const handleToolCall = async (params: IToolHandlerParams) => {
166
+ await checkToolAccess(params.name, params.payload);
167
+ // ... tool switch logic
483
168
  };
484
-
485
- async function handleGetPublicData(args: any): Promise<any> {
486
- return formatToolResult({ message: 'Public data retrieved', data: { public: true } });
487
- }
488
-
489
- async function handleGetUserData(args: any): Promise<any> {
490
- return formatToolResult({ message: 'User data retrieved', data: args });
491
- }
492
-
493
- async function handleModifyData(args: any): Promise<any> {
494
- return formatToolResult({ message: 'Data modified', modified: args });
495
- }
496
-
497
- async function handleAdminOperation(args: any): Promise<any> {
498
- return formatToolResult({ message: 'Admin operation completed', operation: args });
499
- }
500
169
  ```
501
170
 
502
- **Result**: Each tool enforces its own AD group requirements:
503
- - `get_public_data` - accessible to everyone (public)
504
- - `get_user_data` - requires `DOMAIN\MCP-Users` group
505
- - `modify_data` - requires `DOMAIN\MCP-DataModifiers` group
506
- - `admin_operation` - requires `DOMAIN\MCP-Admins` group
507
-
508
- If a user tries to call a tool without being in the required group:
509
- ```json
510
- {
511
- "jsonrpc": "2.0",
512
- "error": {
513
- "code": -32603,
514
- "message": "Forbidden: User 'john.doe' is not authorized to use tool 'admin_operation'. Required group: 'DOMAIN\\MCP-Admins'"
515
- },
516
- "id": 1
517
- }
518
- ```
519
-
520
- ---
521
-
522
- ## Summary: Authorization Levels
171
+ ## Authorization Levels Summary
523
172
 
524
173
  | Level | Location | Error Type | Use Case |
525
174
  |-------|----------|------------|----------|
526
- | HTTP Server | `customAuthValidator` | HTTP 403 Forbidden | Block unauthorized users completely |
527
- | All Tools | `toolHandler` (global check) | MCP Tool Error | Allow HTTP access, restrict all tool usage |
528
- | Specific Tool | `toolHandler` (per-tool check) | MCP Tool Error | Fine-grained tool-level permissions |
175
+ | HTTP Server | `customAuthValidator` | HTTP 403 | Block completely |
176
+ | All Tools | `toolHandler` (global) | MCP Error | Allow HTTP, restrict tools |
177
+ | Per Tool | `toolHandler` (per-tool) | MCP Error | Fine-grained permissions |