fa-mcp-sdk 0.2.182 → 0.2.192

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 (31) hide show
  1. package/cli-template/.claude/agents/fa-mcp-sdk.md +158 -0
  2. package/cli-template/FA-MCP-SDK-DOC/00-FA-MCP-SDK-index.md +216 -0
  3. package/cli-template/FA-MCP-SDK-DOC/01-getting-started.md +209 -0
  4. package/cli-template/FA-MCP-SDK-DOC/02-tools-and-api.md +321 -0
  5. package/cli-template/FA-MCP-SDK-DOC/03-configuration.md +415 -0
  6. package/cli-template/FA-MCP-SDK-DOC/04-authentication.md +544 -0
  7. package/cli-template/FA-MCP-SDK-DOC/05-ad-authorization.md +476 -0
  8. package/cli-template/FA-MCP-SDK-DOC/06-utilities.md +394 -0
  9. package/cli-template/FA-MCP-SDK-DOC/07-testing-and-operations.md +171 -0
  10. package/dist/core/_types_/types.d.ts +0 -5
  11. package/dist/core/_types_/types.d.ts.map +1 -1
  12. package/dist/core/index.d.ts +2 -1
  13. package/dist/core/index.d.ts.map +1 -1
  14. package/dist/core/index.js +2 -0
  15. package/dist/core/index.js.map +1 -1
  16. package/dist/core/web/home-api.js +1 -1
  17. package/dist/core/web/home-api.js.map +1 -1
  18. package/dist/core/web/openapi.d.ts +64 -0
  19. package/dist/core/web/openapi.d.ts.map +1 -0
  20. package/dist/core/web/openapi.js +235 -0
  21. package/dist/core/web/openapi.js.map +1 -0
  22. package/dist/core/web/server-http.d.ts.map +1 -1
  23. package/dist/core/web/server-http.js +11 -9
  24. package/dist/core/web/server-http.js.map +1 -1
  25. package/dist/core/web/static/home/index.html +4 -2
  26. package/dist/core/web/static/home/script.js +2 -2
  27. package/package.json +9 -12
  28. package/src/template/api/router.ts +66 -4
  29. package/src/template/start.ts +0 -5
  30. package/cli-template/FA-MCP-SDK.md +0 -2540
  31. package/src/template/api/swagger.ts +0 -167
@@ -0,0 +1,321 @@
1
+ # Tools and REST API Development
2
+
3
+ ## Tool Development
4
+
5
+ ### Tool Definition in `src/tools/tools.ts`
6
+
7
+ ```typescript
8
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
9
+ import { IToolInputSchema } from 'fa-mcp-sdk';
10
+
11
+ export const tools: Tool[] = [
12
+ {
13
+ name: 'my_custom_tool',
14
+ description: 'Description of what this tool does',
15
+ inputSchema: {
16
+ type: 'object',
17
+ properties: {
18
+ query: {
19
+ type: 'string',
20
+ description: 'Input query or text',
21
+ },
22
+ options: {
23
+ type: 'object',
24
+ description: 'Optional configuration',
25
+ },
26
+ },
27
+ required: ['query'],
28
+ },
29
+ },
30
+ ];
31
+ ```
32
+
33
+ ### Tool Handler in `src/tools/handle-tool-call.ts`
34
+
35
+ ```typescript
36
+ import { formatToolResult, ToolExecutionError, logger } from 'fa-mcp-sdk';
37
+
38
+ export const handleToolCall = async (params: { name: string, arguments?: any, headers?: Record<string, string> }): Promise<any> => {
39
+ const { name, arguments: args, headers } = params;
40
+
41
+ logger.info(`Tool called: ${name}`);
42
+
43
+ // Access normalized HTTP headers (all header names are lowercase)
44
+ if (headers) {
45
+ const authHeader = headers.authorization;
46
+ const userAgent = headers['user-agent'];
47
+ const customHeader = headers['x-custom-header'];
48
+ logger.info(`Headers available: authorization=${!!authHeader}, user-agent=${userAgent}`);
49
+ }
50
+
51
+ try {
52
+ switch (name) {
53
+ case 'my_custom_tool':
54
+ return await handleMyCustomTool(args);
55
+
56
+ default:
57
+ throw new ToolExecutionError(name, `Unknown tool: ${name}`);
58
+ }
59
+ } catch (error) {
60
+ logger.error(`Tool execution failed for ${name}:`, error);
61
+ throw error;
62
+ }
63
+ };
64
+
65
+ async function handleMyCustomTool(args: any): Promise<string> {
66
+ const { query, options } = args || {};
67
+
68
+ if (!query) {
69
+ throw new ToolExecutionError('my_custom_tool', 'Query parameter is required');
70
+ }
71
+
72
+ // Your tool logic here
73
+ const result = {
74
+ message: `Processed: ${query}`,
75
+ timestamp: new Date().toISOString(),
76
+ options: options || {},
77
+ };
78
+
79
+ return formatToolResult(result);
80
+ }
81
+ ```
82
+
83
+ ### HTTP Headers in Tool Handler
84
+
85
+ The FA-MCP-SDK automatically passes normalized HTTP headers to your `toolHandler` function, enabling context-aware tool execution based on client information.
86
+
87
+ **Key Features:**
88
+ - All headers are automatically normalized to lowercase
89
+ - Available in both HTTP and SSE transports (SSE provides empty headers object)
90
+ - Headers are sanitized and only string values are passed
91
+ - Array header values are joined with `', '` separator
92
+
93
+ **Example Usage:**
94
+
95
+ ```typescript
96
+ export const handleToolCall = async (params: {
97
+ name: string,
98
+ arguments?: any,
99
+ headers?: Record<string, string>
100
+ }): Promise<any> => {
101
+ const { name, arguments: args, headers } = params;
102
+
103
+ // Access client information via headers
104
+ if (headers) {
105
+ const authHeader = headers.authorization; // Lowercase normalized
106
+ const userAgent = headers['user-agent']; // Browser/client info
107
+ const clientIP = headers['x-real-ip'] || headers['x-forwarded-for']; // Proxy headers
108
+ const customData = headers['x-custom-header']; // Custom headers
109
+
110
+ logger.info(`Tool ${name} called by ${userAgent} from IP ${clientIP}`);
111
+
112
+ // Conditional logic based on client
113
+ if (userAgent?.includes('mobile')) {
114
+ return await handleMobileRequest(args);
115
+ }
116
+
117
+ // Custom authorization beyond standard auth
118
+ if (customData === 'admin-mode' && authHeader) {
119
+ return await handleAdminRequest(args);
120
+ }
121
+ }
122
+
123
+ // Regular tool logic
124
+ switch (name) {
125
+ case 'get_user_data':
126
+ // Use headers for audit logging
127
+ return await getUserData(args, {
128
+ clientIP: headers?.['x-real-ip'],
129
+ userAgent: headers?.['user-agent']
130
+ });
131
+ }
132
+ };
133
+ ```
134
+
135
+ **Header Normalization Details:**
136
+
137
+ ```typescript
138
+ // Original headers from client:
139
+ {
140
+ 'Authorization': 'Bearer token123',
141
+ 'X-Custom-Header': 'value',
142
+ 'USER-AGENT': 'MyClient/1.0'
143
+ }
144
+
145
+ // Normalized headers passed to toolHandler:
146
+ {
147
+ 'authorization': 'Bearer token123',
148
+ 'x-custom-header': 'value',
149
+ 'user-agent': 'MyClient/1.0'
150
+ }
151
+ ```
152
+
153
+ **Transport Differences:**
154
+
155
+ - **HTTP Transport**: Full headers available from Express request object
156
+ - **SSE Transport**: Headers preserved from initial SSE connection establishment (GET /sse request)
157
+
158
+ **Common Use Cases:**
159
+ - Client identification and analytics
160
+ - Custom authorization checks beyond standard authentication
161
+ - Request routing based on client capabilities
162
+ - Audit logging with client context
163
+ - Rate limiting per client type
164
+
165
+ ---
166
+
167
+ ## REST API Endpoints
168
+
169
+ The SDK supports custom REST API endpoints alongside MCP tools. Define your endpoints in `src/api/router.ts` using [tsoa](https://tsoa-community.github.io/docs/) decorators for automatic OpenAPI/Swagger documentation generation.
170
+
171
+ ### OpenAPI Generation
172
+
173
+ **Swagger is generated automatically** when the server starts if `swagger/openapi.yaml` doesn't exist. The specification is built from tsoa-decorated controllers in your `src/api/` directory.
174
+
175
+ - **Swagger UI**: Available at `/docs`
176
+ - **OpenAPI spec**: Available at `/api/openapi.json` and `/api/openapi.yaml`
177
+
178
+ To regenerate the spec, simply delete `swagger/openapi.yaml` and restart the server.
179
+
180
+ ### Basic Controller Example
181
+
182
+ **`src/api/router.ts`:**
183
+ ```typescript
184
+ import { Router } from 'express';
185
+ import { Route, Get, Post, Body, Tags, Query } from 'tsoa';
186
+ import { logger } from 'fa-mcp-sdk';
187
+
188
+ export const apiRouter: Router = Router();
189
+
190
+ // Response interfaces for tsoa (used in OpenAPI schema generation)
191
+ export interface UserResponse {
192
+ id: string;
193
+ name: string;
194
+ email: string;
195
+ }
196
+
197
+ export interface CreateUserRequest {
198
+ name: string;
199
+ email: string;
200
+ }
201
+
202
+ /**
203
+ * User Management Controller
204
+ * All methods in this class will be under /api prefix
205
+ */
206
+ @Route('api')
207
+ export class UserController {
208
+ /**
209
+ * Get user by ID
210
+ * @param userId The user's unique identifier
211
+ */
212
+ @Get('users/{userId}')
213
+ @Tags('Users')
214
+ public async getUser(userId: string): Promise<UserResponse> {
215
+ logger.info(`Getting user: ${userId}`);
216
+ return {
217
+ id: userId,
218
+ name: 'John Doe',
219
+ email: 'john@example.com',
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Create a new user
225
+ */
226
+ @Post('users')
227
+ @Tags('Users')
228
+ public async createUser(
229
+ @Body() body: CreateUserRequest
230
+ ): Promise<UserResponse> {
231
+ logger.info(`Creating user: ${body.name}`);
232
+ return {
233
+ id: 'new-user-id',
234
+ name: body.name,
235
+ email: body.email,
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Search users by query
241
+ */
242
+ @Get('users')
243
+ @Tags('Users')
244
+ public async searchUsers(
245
+ @Query() query?: string,
246
+ @Query() limit?: number
247
+ ): Promise<UserResponse[]> {
248
+ logger.info(`Searching users: ${query}, limit: ${limit}`);
249
+ return [];
250
+ }
251
+ }
252
+ ```
253
+
254
+ ### Tags Organization
255
+
256
+ Use `@Tags()` decorator to organize endpoints in Swagger UI:
257
+
258
+ ```typescript
259
+ @Route('api')
260
+ export class MyController {
261
+ // This endpoint appears in "Users" section
262
+ @Get('users')
263
+ @Tags('Users')
264
+ public async listUsers(): Promise<User[]> { ... }
265
+
266
+ // This endpoint appears in "Admin" section
267
+ @Get('admin/stats')
268
+ @Tags('Admin')
269
+ public async getStats(): Promise<Stats> { ... }
270
+
271
+ // This endpoint appears in BOTH sections
272
+ @Get('admin/users')
273
+ @Tags('Admin', 'Users')
274
+ public async adminListUsers(): Promise<User[]> { ... }
275
+ }
276
+ ```
277
+
278
+ **Important**: Apply `@Tags()` to individual methods, not the class. Class-level `@Tags()` applies to ALL methods, which may cause unintended grouping.
279
+
280
+ ### Common tsoa Decorators
281
+
282
+ | Decorator | Usage | Example |
283
+ |-----------|-------|---------|
284
+ | `@Route('prefix')` | Set route prefix | `@Route('api')` |
285
+ | `@Get('path')` | GET endpoint | `@Get('users/{id}')` |
286
+ | `@Post('path')` | POST endpoint | `@Post('users')` |
287
+ | `@Put('path')` | PUT endpoint | `@Put('users/{id}')` |
288
+ | `@Delete('path')` | DELETE endpoint | `@Delete('users/{id}')` |
289
+ | `@Tags('name')` | Swagger section | `@Tags('Users')` |
290
+ | `@Body()` | Request body | `@Body() data: CreateRequest` |
291
+ | `@Query()` | Query parameter | `@Query() search?: string` |
292
+ | `@Path()` | Path parameter | `@Path() id: string` |
293
+ | `@Header()` | Header value | `@Header('x-api-key') apiKey: string` |
294
+
295
+ ### Manual Express Routes
296
+
297
+ For endpoints not requiring OpenAPI documentation, use standard Express routing:
298
+
299
+ ```typescript
300
+ import { Router } from 'express';
301
+ import { createAuthMW } from 'fa-mcp-sdk';
302
+
303
+ export const apiRouter: Router = Router();
304
+
305
+ const authMW = createAuthMW();
306
+
307
+ // Manual route with authentication
308
+ apiRouter.get('/internal/status', authMW, (req, res) => {
309
+ res.json({ status: 'ok', timestamp: Date.now() });
310
+ });
311
+
312
+ // tsoa controllers are still processed for OpenAPI generation
313
+ @Route('api')
314
+ export class PublicController {
315
+ @Get('health')
316
+ @Tags('Server')
317
+ public async health(): Promise<{ status: string }> {
318
+ return { status: 'healthy' };
319
+ }
320
+ }
321
+ ```