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,415 @@
1
+ # Configuration, Cache, and Database
2
+
3
+ ## Configuration Management
4
+
5
+ ### Using `appConfig`
6
+
7
+ Access configuration in your code:
8
+
9
+ ```typescript
10
+ import { appConfig } from 'fa-mcp-sdk';
11
+
12
+ // Access configuration values
13
+ const serverPort = appConfig.webServer.port;
14
+ const dbEnabled = appConfig.isMainDBUsed;
15
+ const transport = appConfig.mcp.transportType; // 'stdio' | 'http'
16
+ ```
17
+
18
+ ### Configuration Files
19
+
20
+ **`config/default.yaml`** - Base configuration:
21
+ ```yaml
22
+ accessPoints:
23
+ myService:
24
+ title: 'My remote service'
25
+ host: <host>
26
+ port: 9999
27
+ token: '***'
28
+ noConsul: true # Use if the service developers do not provide registration in consul
29
+ consulServiceName: <consulServiceName>
30
+
31
+ # --------------------------------------------------
32
+ # CACHING Reduces API calls by caching responses
33
+ # --------------------------------------------------
34
+ cache:
35
+ # Default Cache TTL in seconds
36
+ ttlSeconds: 300
37
+ # Default maximum number of cached items
38
+ maxItems: 1000
39
+
40
+ consul:
41
+ check:
42
+ interval: '10s'
43
+ timeout: '5s'
44
+ deregistercriticalserviceafter: '3m'
45
+ agent:
46
+ # Credentials for getting information about services in the DEV DC
47
+ dev:
48
+ dc: '{{consul.agent.dev.dc}}'
49
+ host: '{{consul.agent.dev.host}}'
50
+ port: 443
51
+ secure: true
52
+ # Token for getting information about DEV services
53
+ token: '***'
54
+ # Credentials for getting information about services in the PROD DC
55
+ prd:
56
+ dc: '{{consul.agent.prd.dc}}'
57
+ host: '{{consul.agent.prd.host}}'
58
+ port: 443
59
+ secure: true
60
+ # Token for obtaining information about PROD services
61
+ token: '***'
62
+ # Credentials for registering the service with Consul
63
+ reg:
64
+ # The host of the consul agent where the service will be registered. If not specified, the server on which the service is running is used
65
+ host: null
66
+ port: 8500
67
+ secure: false
68
+ # Token for registering the service in the consul agent
69
+ token: '***'
70
+ service:
71
+ enable: {{consul.service.enable}} # true - Allows registration of the service with the consul
72
+ name: <name> # <name> will be replaced by <package.json>.name at initialization
73
+ instance: '{{SERVICE_INSTANCE}}' # This value will be specified as a suffix in the id of the service
74
+ version: <version> # <version> will be replaced by <package.json>.version at initialization
75
+ description: <description> # <description> will be replaced by <package.json>.description at initialization
76
+ tags: [] # If null or empty array - Will be pulled up from package.keywords at initialization
77
+ meta:
78
+ # "Home" page link template
79
+ who: 'http://{address}:{port}/'
80
+ envCode: # Used to generate the service ID
81
+ prod: {{consul.envCode.prod}} # Production environment code
82
+ dev: {{consul.envCode.dev}} # Development environment code
83
+
84
+ db:
85
+ postgres:
86
+ dbs:
87
+ main:
88
+ label: 'My Database'
89
+ host: '' # To exclude the use of the database, you need to set host = ''
90
+ port: 5432
91
+ database: <database>
92
+ user: <user>
93
+ password: <password>
94
+ usedExtensions: []
95
+
96
+ logger:
97
+ level: info
98
+ useFileLogger: {{logger.useFileLogger}} # To use or not to use logging to a file
99
+ # Absolute path to the folder where logs will be written. Default <proj_root>/../logs
100
+ dir: '{{logger.dir}}'
101
+
102
+ mcp:
103
+ transportType: http # stdio | http
104
+ # Response format configuration.
105
+ # - structuredContent - default - the response in result.structuredContent returns JSON
106
+ # - text - in the response, serialized JSON is returned in result.content[0].text
107
+ toolAnswerAs: text # text | structuredContent
108
+ rateLimit:
109
+ maxRequests: 100
110
+ windowMs: 60000 # 1 minute
111
+
112
+ swagger:
113
+ servers: # An array of servers that will be added to swagger docs
114
+ # - url: http://localhost:9020
115
+ # description: "Development server (localhost)"
116
+ # - url: http://0.0.0.0:9020
117
+ # description: "Development server (all interfaces)"
118
+ # - url: http://<prod_server_host_or_ip>:{{port}}
119
+ # description: "PROD server"
120
+ - url: https://{{mcp.domain}}
121
+ description: "PROD server"
122
+
123
+ uiColor:
124
+ # Font color of the header and a number of interface elements on the HOME page
125
+ primary: '#0f65dc'
126
+
127
+ webServer:
128
+ host: '0.0.0.0'
129
+ port: {{port}}
130
+ # array of hosts that CORS skips
131
+ originHosts: ['localhost', '0.0.0.0']
132
+ # Authentication is configured here only when accessing the MCP server
133
+ # Authentication in services that enable tools, resources, and prompts
134
+ # is implemented more deeply. To do this, you need to use the information passed in HTTP headers
135
+ # You can also use a custom authorization function
136
+ auth:
137
+ enabled: false # Enables/disables authorization
138
+ # ========================================================================
139
+ # PERMANENT SERVER TOKENS
140
+ # Static tokens for server-to-server communication
141
+ # CPU cost: O(1) - fastest authentication method
142
+ #
143
+ # To enable this authentication, you need to set auth.enabled = true
144
+ # and set one token of at least 20 characters in length
145
+ # ========================================================================
146
+ permanentServerTokens: [ ] # Add your server tokens here: ['token1', 'token2']
147
+
148
+ # ========================================================================
149
+ # JWT TOKEN WITH SYMMETRIC ENCRYPTION
150
+ # Custom JWT tokens with AES-256 encryption
151
+ # CPU cost: Medium - decryption + JSON parsing
152
+ #
153
+ # To enable this authentication, you need to set auth.enabled = true and set
154
+ # encryptKey to at least 20 characters
155
+ # ========================================================================
156
+ jwtToken:
157
+ # Symmetric encryption key to generate a token for this MCP (minimum 8 chars)
158
+ encryptKey: '***'
159
+ # If webServer.auth.enabled and the parameter true, the service name and the service specified in the token will be checked
160
+ checkMCPName: true
161
+
162
+ # ========================================================================
163
+ # Basic Authentication - Base64 encoded username:password
164
+ # CPU cost: Medium - Base64 decoding + string comparison
165
+ # To enable this authentication, you need to set auth.enabled = true
166
+ # and set username and password to valid values
167
+ # ========================================================================
168
+ basic:
169
+ username: ''
170
+ password: '***'
171
+ ```
172
+
173
+ **`config/local.yaml`** - local overrides. Usually contains secrets.
174
+
175
+ ---
176
+
177
+ ## Cache Management
178
+
179
+ ### `getCache(options?): CacheManager`
180
+
181
+ Get or create a global cache instance for your MCP server.
182
+
183
+ ```typescript
184
+ import { getCache, CacheManager } from 'fa-mcp-sdk';
185
+
186
+ // Create default cache instance
187
+ const cache = getCache();
188
+
189
+ // Create cache with custom options
190
+ const customCache = getCache({
191
+ ttlSeconds: 600, // Default TTL: 10 minutes
192
+ maxItems: 5000, // Max cached items
193
+ checkPeriod: 300, // Cleanup interval in seconds
194
+ verbose: true // Enable debug logging
195
+ });
196
+ ```
197
+
198
+ ### Cache Methods
199
+
200
+ The `CacheManager` provides the following methods:
201
+
202
+ | Method | Description | Example |
203
+ |--------|-------------|---------|
204
+ | `get<T>(key)` | Get value from cache | `const user = cache.get<User>('user:123');` |
205
+ | `set<T>(key, value, ttl?)` | Set value in cache | `cache.set('user:123', userData, 300);` |
206
+ | `has(key)` | Check if key exists | `if (cache.has('user:123')) { ... }` |
207
+ | `del(key)` | Delete key from cache | `cache.del('user:123');` |
208
+ | `take<T>(key)` | Get and delete (single use) | `const otp = cache.take<string>('otp:123');` |
209
+ | `mget<T>(keys[])` | Get multiple values | `const users = cache.mget(['user:1', 'user:2']);` |
210
+ | `mset(items[])` | Set multiple values | `cache.mset([{key: 'a', val: 1}, {key: 'b', val: 2}]);` |
211
+ | `getOrSet<T>(key, factory, ttl?)` | Get or compute value | `const data = await cache.getOrSet('key', () => fetchData());` |
212
+ | `keys()` | List all keys | `const allKeys = cache.keys();` |
213
+ | `flush()` | Clear all entries | `cache.flush();` |
214
+ | `ttl(key, seconds)` | Update key TTL | `cache.ttl('user:123', 600);` |
215
+ | `getTtl(key)` | Get remaining TTL | `const remaining = cache.getTtl('user:123');` |
216
+ | `getStats()` | Get cache statistics | `const stats = cache.getStats();` |
217
+ | `close()` | Close cache resources | `cache.close();` |
218
+
219
+ ### Usage Examples
220
+
221
+ ```typescript
222
+ import { getCache } from 'fa-mcp-sdk';
223
+
224
+ const cache = getCache();
225
+
226
+ // Basic caching
227
+ cache.set('user:123', { name: 'John', email: 'john@example.com' });
228
+ const user = cache.get<User>('user:123');
229
+
230
+ // Cache with TTL (time to live)
231
+ cache.set('session:abc', sessionData, 1800); // 30 minutes
232
+
233
+ // Single-use values (OTP, tokens)
234
+ cache.set('otp:user123', '123456', 300);
235
+ const otp = cache.take('otp:user123'); // Gets and deletes
236
+
237
+ // Get-or-set pattern
238
+ const expensiveData = await cache.getOrSet(
239
+ 'computation:key',
240
+ async () => {
241
+ // This function runs only on cache miss
242
+ return await performExpensiveOperation();
243
+ },
244
+ 3600 // Cache for 1 hour
245
+ );
246
+
247
+ // Batch operations
248
+ const userData = cache.mget(['user:1', 'user:2', 'user:3']);
249
+ cache.mset([
250
+ { key: 'user:1', val: user1Data },
251
+ { key: 'user:2', val: user2Data, ttl: 600 }
252
+ ]);
253
+
254
+ // Cache monitoring
255
+ const stats = cache.getStats();
256
+ console.log(`Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
257
+ console.log(`Keys: ${stats.keys}, Memory: ${stats.vsize} bytes`);
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Database Integration
263
+
264
+ To disable the use of the database, you need to set appConfig.db.postgres.dbs.main.host to an empty value.
265
+ In this case, when the configuration is formed, appConfig.isMainDBUsed is set to false.
266
+
267
+
268
+ If you enable database support (`isMainDBUsed: true` in config):
269
+
270
+ ```typescript
271
+ import {
272
+ queryMAIN,
273
+ execMAIN,
274
+ oneRowMAIN,
275
+ getMainDBConnectionStatus
276
+ } from 'fa-mcp-sdk';
277
+
278
+ // Check database connection. If there is no connection, the application stops
279
+ await checkMainDB();
280
+
281
+ // queryMAIN - the main function of executing SQL queries to the main database
282
+
283
+ // Function Signature:
284
+ const queryMAIN = async <R extends QueryResultRow = any> (
285
+ arg: string | IQueryPgArgsCOptional,
286
+ sqlValues?: any[],
287
+ throwError = false,
288
+ ): Promise<QueryResult<R> | undefined> {...}
289
+
290
+ // Types used:
291
+ export interface IQueryPgArgs {
292
+ connectionId: string,
293
+ poolConfig?: PoolConfig & IDbOptionsPg,
294
+ client?: IPoolPg,
295
+ sqlText: string,
296
+ sqlValues?: any[],
297
+ throwError?: boolean,
298
+ prefix?: string,
299
+ registerTypesFunctions?: IRegisterTypeFn[],
300
+ }
301
+ export interface IQueryPgArgsCOptional extends Omit<IQueryPgArgs, 'connectionId'> {
302
+ connectionId?: string
303
+ }
304
+
305
+ // Examples of use
306
+ const users1 = await queryMAIN('SELECT * FROM users WHERE active = $1', [true]);
307
+ // Alternative use case
308
+ const users2 = await queryMAIN({ sqlText: 'SELECT * FROM users WHERE active = $1', sqlValues: [true] });
309
+
310
+
311
+ // execMAIN - execute SQL commands without returning result set
312
+ // Function Signature:
313
+ const execMAIN = async (
314
+ arg: string | IQueryPgArgsCOptional,
315
+ ): Promise<number | undefined> {...}
316
+
317
+ // Examples:
318
+ await execMAIN('INSERT INTO logs (message, created_at) VALUES ($1, $2)',
319
+ ['Server started', new Date()]);
320
+ await execMAIN({ sqlText: 'UPDATE users SET active = $1 WHERE id = $2', sqlValues: [false, userId] });
321
+
322
+ // queryRsMAIN - execute SQL and return rows array directly
323
+ // Function Signature:
324
+ const queryRsMAIN = async <R extends QueryResultRow = any> (
325
+ arg: string | IQueryPgArgsCOptional,
326
+ sqlValues?: any[],
327
+ throwError = false,
328
+ ): Promise<R[] | undefined> {...}
329
+
330
+ // Example:
331
+ const users = await queryRsMAIN<User>('SELECT * FROM users WHERE active = $1', [true]);
332
+
333
+ // oneRowMAIN - execute SQL and return single row
334
+ // Function Signature:
335
+ const oneRowMAIN = async <R extends QueryResultRow = any> (
336
+ arg: string | IQueryPgArgsCOptional,
337
+ sqlValues?: any[],
338
+ throwError = false,
339
+ ): Promise<R | undefined> {...}
340
+
341
+ // Example:
342
+ const user = await oneRowMAIN<User>('SELECT * FROM users WHERE id = $1', [userId]);
343
+
344
+ // getMainDBConnectionStatus - check database connection status
345
+ // Function Signature:
346
+ const getMainDBConnectionStatus = async (): Promise<string> {...}
347
+
348
+ // Possible return values: 'connected' | 'disconnected' | 'error' | 'db_not_used'
349
+ const status = await getMainDBConnectionStatus();
350
+
351
+ // checkMainDB - verify database connectivity (stops application if failed)
352
+ // Function Signature:
353
+ const checkMainDB = async (): Promise<void> {...}
354
+
355
+ // Example:
356
+ await checkMainDB(); // Throws or exits process if DB connection fails
357
+
358
+ // getInsertSqlMAIN - generate INSERT SQL statement
359
+ // Function Signature:
360
+ const getInsertSqlMAIN = async <U extends TDBRecord = TDBRecord> (arg: {
361
+ commonSchemaAndTable: string,
362
+ recordset: TRecordSet<U>,
363
+ excludeFromInsert?: string[],
364
+ addOutputInserted?: boolean,
365
+ isErrorOnConflict?: boolean,
366
+ keepSerialFields?: boolean,
367
+ }): Promise<string> {...}
368
+
369
+ // Example:
370
+ const insertSql = await getInsertSqlMAIN({
371
+ commonSchemaAndTable: 'public.users',
372
+ recordset: [{ name: 'John', email: 'john@example.com' }],
373
+ addOutputInserted: true
374
+ });
375
+
376
+ // getMergeSqlMAIN - generate UPSERT (INSERT...ON CONFLICT) SQL statement
377
+ // Function Signature:
378
+ const getMergeSqlMAIN = async <U extends TDBRecord = TDBRecord> (arg: {
379
+ commonSchemaAndTable: string,
380
+ recordset: TRecordSet<U>,
381
+ conflictFields?: string[],
382
+ omitFields?: string[],
383
+ updateFields?: string[],
384
+ fieldsExcludedFromUpdatePart?: string[],
385
+ noUpdateIfNull?: boolean,
386
+ mergeCorrection?: (_sql: string) => string,
387
+ returning?: string,
388
+ }): Promise<string> {...}
389
+
390
+ // Example:
391
+ const mergeSql = await getMergeSqlMAIN({
392
+ commonSchemaAndTable: 'public.users',
393
+ recordset: [{ id: 1, name: 'John Updated', email: 'john@example.com' }],
394
+ conflictFields: ['email'],
395
+ returning: '*'
396
+ });
397
+
398
+ // mergeByBatch - execute merge operations in batches
399
+ // Function Signature:
400
+ const mergeByBatch = async <U extends TDBRecord = TDBRecord> (arg: {
401
+ recordset: TRecordSet<U>,
402
+ getMergeSqlFn: Function
403
+ batchSize?: number
404
+ }): Promise<any[]> {...}
405
+
406
+ // Example:
407
+ const results = await mergeByBatch({
408
+ recordset: largeDataSet,
409
+ getMergeSqlFn: (batch) => getMergeSqlMAIN({
410
+ commonSchemaAndTable: 'public.users',
411
+ recordset: batch
412
+ }),
413
+ batchSize: 500
414
+ });
415
+ ```