tmcp 1.10.2 → 1.10.3

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.
package/README.md CHANGED
@@ -249,6 +249,67 @@ Send notifications for subscriptions.
249
249
  server.changed('resource', 'file://path/to/resource');
250
250
  ```
251
251
 
252
+ ##### `progress(progress, total?, message?)`
253
+
254
+ Report progress during long-running operations. Progress notifications are only sent when a progress token is provided by the client in the request's `_meta.progressToken` field.
255
+
256
+ ```javascript
257
+ server.tool(
258
+ {
259
+ name: 'process-large-file',
260
+ description: 'Process a large file with progress updates',
261
+ },
262
+ async (input) => {
263
+ const totalSteps = 100;
264
+
265
+ for (let i = 0; i <= totalSteps; i++) {
266
+ // Report progress with current step, total steps, and optional message
267
+ server.progress(
268
+ i,
269
+ totalSteps,
270
+ `Processing step ${i}/${totalSteps}`,
271
+ );
272
+
273
+ // Simulate work
274
+ await processStep(i);
275
+ }
276
+
277
+ return {
278
+ content: [{ type: 'text', text: 'Processing complete!' }],
279
+ };
280
+ },
281
+ );
282
+ ```
283
+
284
+ **Parameters:**
285
+
286
+ - `progress` (number): Current progress value (should be ≤ total and always increase)
287
+ - `total` (number, optional): Maximum progress value (defaults to 1)
288
+ - `message` (string, optional): Descriptive message about current progress
289
+
290
+ **Notes:**
291
+
292
+ - Progress notifications are only sent when the client provides a `progressToken` in the request
293
+ - Progress values should be monotonically increasing within a single operation
294
+ - Each session maintains its own progress context
295
+
296
+ ##### `log(level, data, logger?)`
297
+
298
+ Send log messages to connected clients when logging is enabled.
299
+
300
+ ```javascript
301
+ // Log at different severity levels
302
+ server.log('info', 'Server started successfully');
303
+ server.log('warning', 'Configuration missing, using defaults');
304
+ server.log('error', 'Failed to connect to database', 'database-logger');
305
+ ```
306
+
307
+ **Parameters:**
308
+
309
+ - `level` (string): Log level ('debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency')
310
+ - `data` (any): Data to log
311
+ - `logger` (string, optional): Logger name/category
312
+
252
313
  ##### `on(event, callback)`
253
314
 
254
315
  Listen to server events.
@@ -265,6 +326,94 @@ server.on('send', ({ request, context }) => {
265
326
 
266
327
  ## Advanced Examples
267
328
 
329
+ ### Progress Reporting
330
+
331
+ Provide real-time progress updates for long-running operations:
332
+
333
+ ```javascript
334
+ server.tool(
335
+ {
336
+ name: 'analyze-codebase',
337
+ description: 'Analyze a large codebase with progress tracking',
338
+ schema: z.object({
339
+ path: z.string(),
340
+ includeTests: z.boolean().default(false),
341
+ }),
342
+ },
343
+ async ({ path, includeTests }) => {
344
+ // Discover files to analyze
345
+ const files = await discoverFiles(path, includeTests);
346
+ const totalFiles = files.length;
347
+
348
+ server.progress(0, totalFiles, 'Starting analysis...');
349
+
350
+ const results = [];
351
+ for (let i = 0; i < files.length; i++) {
352
+ const file = files[i];
353
+
354
+ // Update progress with current file being processed
355
+ server.progress(
356
+ i + 1,
357
+ totalFiles,
358
+ `Analyzing ${file} (${i + 1}/${totalFiles})`,
359
+ );
360
+
361
+ // Analyze the file
362
+ const analysis = await analyzeFile(file);
363
+ results.push(analysis);
364
+ }
365
+
366
+ // Final progress update
367
+ server.progress(totalFiles, totalFiles, 'Analysis complete!');
368
+
369
+ return {
370
+ content: [
371
+ {
372
+ type: 'text',
373
+ text: `Analyzed ${totalFiles} files. Found ${results.length} issues.`,
374
+ },
375
+ ],
376
+ structuredContent: {
377
+ totalFiles,
378
+ results,
379
+ issues: results.length,
380
+ },
381
+ };
382
+ },
383
+ );
384
+
385
+ // Progress also works in prompts and resources
386
+ server.prompt(
387
+ {
388
+ name: 'generate-report',
389
+ description: 'Generate a comprehensive report',
390
+ schema: z.object({
391
+ sections: z.array(z.string()),
392
+ }),
393
+ },
394
+ async ({ sections }) => {
395
+ const messages = [];
396
+
397
+ for (let i = 0; i < sections.length; i++) {
398
+ server.progress(
399
+ i,
400
+ sections.length,
401
+ `Generating section: ${sections[i]}`,
402
+ );
403
+
404
+ const content = await generateSection(sections[i]);
405
+ messages.push({
406
+ role: 'user',
407
+ content: { type: 'text', text: content },
408
+ });
409
+ }
410
+
411
+ server.progress(sections.length, sections.length, 'Report complete');
412
+ return { messages };
413
+ },
414
+ );
415
+ ```
416
+
268
417
  ### Client Interaction Features
269
418
 
270
419
  ```javascript
@@ -625,6 +774,232 @@ server.tool(
625
774
  );
626
775
  ```
627
776
 
777
+ ## Dynamic Enabling/Disabling
778
+
779
+ All MCP capabilities (tools, prompts, resources, templates) support dynamic enabling and disabling through the `enabled` function. This allows you to conditionally show or hide capabilities based on runtime conditions, user permissions, or any other logic.
780
+
781
+ ### Basic Usage
782
+
783
+ The `enabled` function is called before each list operation and determines whether the capability should be included in the response:
784
+
785
+ ```javascript
786
+ server.tool(
787
+ {
788
+ name: 'admin-only-tool',
789
+ description: 'Administrative tool',
790
+ enabled: () => {
791
+ // Only show this tool if user is admin
792
+ return getCurrentUser().isAdmin;
793
+ },
794
+ },
795
+ async (input) => {
796
+ return { content: [{ type: 'text', text: 'Admin action completed' }] };
797
+ },
798
+ );
799
+ ```
800
+
801
+ ### Async Enabled Functions
802
+
803
+ The `enabled` function can be synchronous or asynchronous:
804
+
805
+ ```javascript
806
+ server.resource(
807
+ {
808
+ name: 'private-document',
809
+ description: 'Access private documents',
810
+ uri: 'private://document.txt',
811
+ enabled: async () => {
812
+ // Check permissions asynchronously
813
+ const user = await getCurrentUser();
814
+ return await hasPermission(user.id, 'read-private-docs');
815
+ },
816
+ },
817
+ async (uri) => {
818
+ return {
819
+ contents: [
820
+ {
821
+ uri,
822
+ mimeType: 'text/plain',
823
+ text: await readPrivateDocument(uri),
824
+ },
825
+ ],
826
+ };
827
+ },
828
+ );
829
+ ```
830
+
831
+ ### Context-Aware Enabling
832
+
833
+ Within the `enabled` function you can read the session context with `server.ctx`, allowing for user-specific or session-specific logic:
834
+
835
+ ```javascript
836
+ server.prompt(
837
+ {
838
+ name: 'personalized-prompt',
839
+ description: 'User-specific prompt template',
840
+ enabled: () => {
841
+ // Access session information
842
+ const sessionId = server.ctx.sessionId;
843
+ const userPrefs = getUserPreferences(sessionId);
844
+ return userPrefs.enablePersonalization;
845
+ },
846
+ },
847
+ async (input) => {
848
+ return {
849
+ messages: [
850
+ {
851
+ role: 'user',
852
+ content: { type: 'text', text: 'Personalized content...' },
853
+ },
854
+ ],
855
+ };
856
+ },
857
+ );
858
+ ```
859
+
860
+ or enable a something based on client capabilities or info
861
+
862
+ ```javascript
863
+ server.prompt(
864
+ {
865
+ name: 'personalized-prompt',
866
+ description: 'Claude Code prompt',
867
+ enabled: () => {
868
+ // Access session information
869
+ const clientInfo = server.currentClientInfo();
870
+ return clientInfo.name === 'Claude Code';
871
+ },
872
+ },
873
+ async (input) => {
874
+ return {
875
+ messages: [
876
+ {
877
+ role: 'user',
878
+ content: { type: 'text', text: 'Personalized content...' },
879
+ },
880
+ ],
881
+ };
882
+ },
883
+ );
884
+ ```
885
+
886
+ or
887
+
888
+ ```javascript
889
+ server.prompt(
890
+ {
891
+ name: 'fetch-repositories',
892
+ description: 'fetch the repositories of the user',
893
+ enabled: () => {
894
+ // Access session information
895
+ const clientInfo = server.currentClientCapabilities();
896
+ return clientInfo.elicitation != null;
897
+ },
898
+ },
899
+ async (input) => {
900
+ const username = await server.elicitation(
901
+ v.object({
902
+ value: v.string(),
903
+ }),
904
+ );
905
+ const repos = await fetchRepos(username.value);
906
+ return {
907
+ messages: [
908
+ {
909
+ role: 'user',
910
+ content: {
911
+ type: 'text',
912
+ text: `Your repositories are ${repos.join(', ')}`,
913
+ },
914
+ },
915
+ ],
916
+ };
917
+ },
918
+ );
919
+ ```
920
+
921
+ ### Template Enabling
922
+
923
+ URI templates also support the `enabled` function:
924
+
925
+ ```javascript
926
+ server.template(
927
+ {
928
+ name: 'user-files',
929
+ description: 'Access user-specific files',
930
+ uri: 'users/{userId}/files/{filename}',
931
+ enabled: async () => {
932
+ // Check if file system is available
933
+ return await isFileSystemMounted();
934
+ },
935
+ complete: {
936
+ userId: (arg, context) => ({
937
+ completion: {
938
+ values: ['user1', 'user2', 'user3'],
939
+ total: 3,
940
+ hasMore: false,
941
+ },
942
+ }),
943
+ },
944
+ },
945
+ async (uri, params) => {
946
+ const content = await readUserFile(params.userId, params.filename);
947
+ return {
948
+ contents: [
949
+ {
950
+ uri,
951
+ mimeType: 'text/plain',
952
+ text: content,
953
+ },
954
+ ],
955
+ };
956
+ },
957
+ );
958
+ ```
959
+
960
+ ### Error Handling
961
+
962
+ If an `enabled` function throws an error, the capability will be treated as disabled:
963
+
964
+ ```javascript
965
+ server.tool(
966
+ {
967
+ name: 'network-tool',
968
+ description: 'Tool requiring network access',
969
+ enabled: () => {
970
+ // If network check fails, tool is disabled
971
+ if (!checkNetworkConnection()) {
972
+ throw new Error('Network unavailable');
973
+ }
974
+ return true;
975
+ },
976
+ },
977
+ async (input) => {
978
+ return {
979
+ content: [{ type: 'text', text: 'Network operation completed' }],
980
+ };
981
+ },
982
+ );
983
+ ```
984
+
985
+ ### Performance Considerations
986
+
987
+ - The `enabled` function is called every time a list is requested
988
+ - Keep enabled functions lightweight to avoid performance issues
989
+ - Consider caching expensive checks when possible
990
+ - Async functions add latency to list operations
991
+
992
+ ### Use Cases
993
+
994
+ Common scenarios where `enabled` functions are useful:
995
+
996
+ 1. **Permission-based access**: Show tools only to authorized users
997
+ 2. **Feature flags**: Enable/disable features based on configuration
998
+ 3. **Resource availability**: Hide resources when underlying systems are unavailable
999
+ 4. **User preferences**: Customize available capabilities per user
1000
+ 5. **Time-based access**: Enable tools only during specific hours
1001
+ 6. **License restrictions**: Limit features based on subscription level
1002
+
628
1003
  ## Contributing
629
1004
 
630
1005
  Contributions are welcome! Please see our [contributing guidelines](../../CONTRIBUTING.md) for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmcp",
3
- "version": "1.10.2",
3
+ "version": "1.10.3",
4
4
  "description": "The main tmcp library",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * @import SqidsType from "sqids";
4
4
  * @import { JSONRPCRequest, JSONRPCParams } from "json-rpc-2.0";
5
5
  * @import { ExtractURITemplateVariables } from "./internal/uri-template.js";
6
- * @import { CallToolResult, ReadResourceResult, GetPromptResult, ClientCapabilities as ClientCapabilitiesType, JSONRPCRequest as JSONRPCRequestType, JSONRPCResponse, CreateMessageRequestParams, CreateMessageResult, Resource, LoggingLevel, ToolAnnotations } from "./validation/index.js";
6
+ * @import { CallToolResult, ReadResourceResult, GetPromptResult, ClientCapabilities as ClientCapabilitiesType, JSONRPCRequest as JSONRPCRequestType, JSONRPCResponse, CreateMessageRequestParams, CreateMessageResult, Resource, LoggingLevel, ToolAnnotations, ClientInfo } from "./validation/index.js";
7
7
  * @import { Tool, Completion, Prompt, StoredResource, ServerOptions, ServerInfo, SubscriptionsKeys, McpEvents } from "./internal/internal.js";
8
8
  */
9
9
  import { JSONRPCClient, JSONRPCServer } from 'json-rpc-2.0';
@@ -77,6 +77,17 @@ async function decode_cursor(cursor) {
77
77
  return decoded;
78
78
  }
79
79
 
80
+ /**
81
+ * @param {()=>boolean | Promise<boolean>} enabled
82
+ */
83
+ async function safe_enabled(enabled) {
84
+ try {
85
+ return await enabled();
86
+ } catch {
87
+ return false;
88
+ }
89
+ }
90
+
80
91
  /**
81
92
  * @typedef {ClientCapabilitiesType} ClientCapabilities
82
93
  */
@@ -129,7 +140,7 @@ export class McpServer {
129
140
  };
130
141
 
131
142
  /**
132
- * @type {AsyncLocalStorage<Context>}
143
+ * @type {AsyncLocalStorage<Context & { progress_token?: string }>}
133
144
  */
134
145
  #ctx_storage = new AsyncLocalStorage();
135
146
 
@@ -138,6 +149,11 @@ export class McpServer {
138
149
  */
139
150
  #client_capabilities_map = new Map();
140
151
 
152
+ /**
153
+ * @type {Map<string|undefined, ClientInfo>}
154
+ */
155
+ #client_info_map = new Map();
156
+
141
157
  /**
142
158
  * @type {Map<string|undefined, string>}
143
159
  */
@@ -195,6 +211,12 @@ export class McpServer {
195
211
  validated_initialize.capabilities,
196
212
  );
197
213
 
214
+ // Store client info
215
+ this.#client_info_map.set(
216
+ session_id,
217
+ validated_initialize.clientInfo,
218
+ );
219
+
198
220
  // Dispatch initialization event
199
221
  this.#event_target.dispatchEvent(
200
222
  new CustomEvent('initialize', {
@@ -258,14 +280,34 @@ export class McpServer {
258
280
  return this.#ctx_storage.getStore()?.sessionId;
259
281
  }
260
282
 
283
+ get #progress_token() {
284
+ return this.#ctx_storage.getStore()?.progress_token;
285
+ }
286
+
287
+ /**
288
+ * The context of the current request, include the session ID and any auth information.
289
+ * @type {Context}
290
+ */
261
291
  get ctx() {
262
- return this.#ctx_storage.getStore() ?? {};
292
+ // eslint-disable-next-line no-unused-vars
293
+ const { progress_token, ...rest } = this.#ctx_storage.getStore() ?? {};
294
+ return rest;
263
295
  }
264
296
 
265
297
  get #client_capabilities() {
266
298
  return this.#client_capabilities_map.get(this.#session_id);
267
299
  }
268
300
 
301
+ /**
302
+ * Get the client information (name, version, etc.) of the client that initiated the current request...useful if you want to do something different based on the client.
303
+ */
304
+ currentClientInfo() {
305
+ return this.#client_info_map.get(this.#session_id);
306
+ }
307
+
308
+ /**
309
+ * Get the client capabilities of the client that initiated the current request, you can use this to verify the client support something before invoking the respective method.
310
+ */
269
311
  currentClientCapabilities() {
270
312
  return this.#client_capabilities;
271
313
  }
@@ -322,34 +364,41 @@ export class McpServer {
322
364
  #init_tools() {
323
365
  if (!this.#options.capabilities?.tools) return;
324
366
  this.#server.addMethod('tools/list', async () => {
325
- const available_tools = await Promise.all(
326
- [...this.#tools].map(async ([name, tool]) => {
327
- return {
328
- name,
329
- title: tool.title || tool.description,
330
- description: tool.description,
331
-
332
- inputSchema: tool.schema
333
- ? await this.#options.adapter.toJsonSchema(
334
- tool.schema,
335
- )
336
- : { type: 'object', properties: {} },
337
- ...(tool.outputSchema
338
- ? {
339
- outputSchema:
340
- await this.#options.adapter.toJsonSchema(
341
- tool.outputSchema,
342
- ),
343
- }
344
- : {}),
345
- ...(tool.annotations
346
- ? {
347
- annotations: tool.annotations,
348
- }
349
- : {}),
350
- };
351
- }),
352
- );
367
+ const available_tools = (
368
+ await Promise.all(
369
+ [...this.#tools].map(async ([name, tool]) => {
370
+ if (
371
+ tool.enabled != null &&
372
+ (await safe_enabled(tool.enabled)) === false
373
+ )
374
+ return null;
375
+ return {
376
+ name,
377
+ title: tool.title || tool.description,
378
+ description: tool.description,
379
+
380
+ inputSchema: tool.schema
381
+ ? await this.#options.adapter.toJsonSchema(
382
+ tool.schema,
383
+ )
384
+ : { type: 'object', properties: {} },
385
+ ...(tool.outputSchema
386
+ ? {
387
+ outputSchema:
388
+ await this.#options.adapter.toJsonSchema(
389
+ tool.outputSchema,
390
+ ),
391
+ }
392
+ : {}),
393
+ ...(tool.annotations
394
+ ? {
395
+ annotations: tool.annotations,
396
+ }
397
+ : {}),
398
+ };
399
+ }),
400
+ )
401
+ ).filter((tool) => tool !== null);
353
402
  return {
354
403
  tools: available_tools,
355
404
  };
@@ -419,39 +468,49 @@ export class McpServer {
419
468
  #init_prompts() {
420
469
  if (!this.#options.capabilities?.prompts) return;
421
470
  this.#server.addMethod('prompts/list', async ({ cursor } = {}) => {
422
- const all_prompts = await Promise.all(
423
- [...this.#prompts].map(async ([name, prompt]) => {
424
- const arguments_schema = prompt.schema
425
- ? await this.#options.adapter.toJsonSchema(
426
- prompt.schema,
427
- )
428
- : {
429
- type: 'object',
430
- properties:
431
- /** @type {Record<string, {description: string}>} */ ({}),
432
- required: [],
433
- };
434
- const keys = Object.keys(arguments_schema.properties ?? {});
435
- const required = arguments_schema.required ?? [];
436
- return {
437
- name,
438
- title: prompt.title || prompt.description,
439
- description: prompt.description,
440
- arguments: keys.map((key) => {
441
- const property = arguments_schema.properties?.[key];
442
- const description =
443
- property && property !== true
444
- ? property.description
445
- : key;
446
- return {
447
- name: key,
448
- required: required.includes(key),
449
- description,
450
- };
451
- }),
452
- };
453
- }),
454
- );
471
+ const all_prompts = (
472
+ await Promise.all(
473
+ [...this.#prompts].map(async ([name, prompt]) => {
474
+ if (
475
+ prompt.enabled != null &&
476
+ (await safe_enabled(prompt.enabled)) === false
477
+ )
478
+ return null;
479
+ const arguments_schema = prompt.schema
480
+ ? await this.#options.adapter.toJsonSchema(
481
+ prompt.schema,
482
+ )
483
+ : {
484
+ type: 'object',
485
+ properties:
486
+ /** @type {Record<string, {description: string}>} */ ({}),
487
+ required: [],
488
+ };
489
+ const keys = Object.keys(
490
+ arguments_schema.properties ?? {},
491
+ );
492
+ const required = arguments_schema.required ?? [];
493
+ return {
494
+ name,
495
+ title: prompt.title || prompt.description,
496
+ description: prompt.description,
497
+ arguments: keys.map((key) => {
498
+ const property =
499
+ arguments_schema.properties?.[key];
500
+ const description =
501
+ property && property !== true
502
+ ? property.description
503
+ : key;
504
+ return {
505
+ name: key,
506
+ required: required.includes(key),
507
+ description,
508
+ };
509
+ }),
510
+ };
511
+ }),
512
+ )
513
+ ).filter((prompt) => prompt !== null);
455
514
 
456
515
  const pagination_options = this.#options.pagination?.prompts;
457
516
  if (!pagination_options || pagination_options.size == null) {
@@ -528,6 +587,11 @@ export class McpServer {
528
587
  for (const [uri, { description, name, title, ...resource }] of this
529
588
  .#resources) {
530
589
  if (!resource.template) {
590
+ if (
591
+ resource.enabled != null &&
592
+ (await safe_enabled(resource.enabled)) === false
593
+ )
594
+ continue;
531
595
  all_resources.push({
532
596
  name,
533
597
  title: title || description,
@@ -535,6 +599,11 @@ export class McpServer {
535
599
  uri,
536
600
  });
537
601
  } else if (resource.list_resources) {
602
+ if (
603
+ resource.enabled != null &&
604
+ (await safe_enabled(resource.enabled)) === false
605
+ )
606
+ continue;
538
607
  const template_resources = await resource.list_resources();
539
608
  all_resources.push(...template_resources);
540
609
  }
@@ -563,20 +632,29 @@ export class McpServer {
563
632
  });
564
633
  this.#server.addMethod('resources/templates/list', async () => {
565
634
  return {
566
- resourceTemplates: [...this.#resources].reduce(
567
- (arr, [uri, { description, name, title, template }]) => {
568
- if (template) {
569
- arr.push({
570
- name,
571
- title: title || description,
572
- description,
573
- uriTemplate: uri,
574
- });
575
- }
576
- return arr;
577
- },
578
- /** @type {Array<{name: string, title: string, description: string, uriTemplate: string}>} */ ([]),
579
- ),
635
+ resourceTemplates: (
636
+ await Promise.all(
637
+ [...this.#resources].map(
638
+ async ([
639
+ uri,
640
+ { description, name, title, template, enabled },
641
+ ]) => {
642
+ if (!template) return null;
643
+ if (
644
+ enabled != null &&
645
+ (await safe_enabled(enabled)) === false
646
+ )
647
+ return null;
648
+ return {
649
+ name,
650
+ title: title || description,
651
+ description,
652
+ uriTemplate: uri,
653
+ };
654
+ },
655
+ ),
656
+ )
657
+ ).filter((resource) => resource != null),
580
658
  };
581
659
  });
582
660
  this.#server.addMethod('resources/read', async ({ uri }) => {
@@ -640,9 +718,7 @@ export class McpServer {
640
718
  this.roots = [];
641
719
  }
642
720
  }
643
- /**
644
- *
645
- */
721
+
646
722
  #init_completion() {
647
723
  this.#server.addMethod(
648
724
  'completion/complete',
@@ -660,9 +736,7 @@ export class McpServer {
660
736
  },
661
737
  );
662
738
  }
663
- /**
664
- *
665
- */
739
+
666
740
  #init_logging() {
667
741
  if (!this.#options.capabilities?.logging) return;
668
742
 
@@ -672,13 +746,25 @@ export class McpServer {
672
746
  });
673
747
  }
674
748
  /**
749
+ * Add a tool to the server. If you want to receive any input you need to provide a schema. The schema needs to be a valid Standard Schema V1 schema and needs to be an Object with the properties you need,
750
+ * Use the description and title to help the LLM to understand what the tool does and when to use it. If you provide an outputSchema, you need to return a structuredContent that matches the schema.
751
+ *
752
+ * Tools will be invoked by the LLM when it thinks it needs to use them, you can use the annotations to provide additional information about the tool, like what it does, how to use it, etc.
675
753
  * @template {StandardSchema | undefined} [TSchema=undefined]
676
754
  * @template {StandardSchema | undefined} [TOutputSchema=undefined]
677
- * @param {{ name: string; description: string; title?: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; outputSchema?: StandardSchemaV1.InferOutput<TOutputSchema extends undefined ? never : TOutputSchema> extends Record<string, unknown> ? TOutputSchema : never; annotations?: ToolAnnotations }} options
755
+ * @param {{ name: string; description: string; title?: string; enabled?: ()=>boolean | Promise<boolean>; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; outputSchema?: StandardSchemaV1.InferOutput<TOutputSchema extends undefined ? never : TOutputSchema> extends Record<string, unknown> ? TOutputSchema : never; annotations?: ToolAnnotations }} options
678
756
  * @param {TSchema extends undefined ? (()=>Promise<CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>> | CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>> | CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>)} execute
679
757
  */
680
758
  tool(
681
- { name, description, title, schema, outputSchema, annotations },
759
+ {
760
+ name,
761
+ description,
762
+ title,
763
+ schema,
764
+ outputSchema,
765
+ annotations,
766
+ enabled,
767
+ },
682
768
  execute,
683
769
  ) {
684
770
  if (this.#options.capabilities?.tools?.listChanged) {
@@ -687,6 +773,7 @@ export class McpServer {
687
773
  this.#tools.set(name, {
688
774
  description,
689
775
  title,
776
+ enabled,
690
777
  schema,
691
778
  outputSchema,
692
779
  execute,
@@ -694,11 +781,16 @@ export class McpServer {
694
781
  });
695
782
  }
696
783
  /**
784
+ * Add a prompt to the server. Prompts are used to provide the user with pre-defined messages that adds context to the LLM.
785
+ * Use the description and title to help the user to understand what the prompt does and when to use it.
786
+ *
787
+ * A prompt can also have a schema that defines the input it expects, the user will be prompted to enter the inputs you request. It can also have a complete function
788
+ * for each input that will be used to provide completions for the user.
697
789
  * @template {StandardSchema | undefined} [TSchema=undefined]
698
- * @param {{ name: string; description: string; title?: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>>> }} options
790
+ * @param {{ name: string; description: string; title?: string; enabled?: ()=>boolean | Promise<boolean>; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>>> }} options
699
791
  * @param {TSchema extends undefined ? (()=>Promise<GetPromptResult> | GetPromptResult) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<GetPromptResult> | GetPromptResult} execute
700
792
  */
701
- prompt({ name, description, title, schema, complete }, execute) {
793
+ prompt({ name, description, title, schema, complete, enabled }, execute) {
702
794
  if (complete) {
703
795
  this.#completions['ref/prompt'].set(name, complete);
704
796
  }
@@ -710,6 +802,7 @@ export class McpServer {
710
802
  title,
711
803
  schema,
712
804
  execute,
805
+ enabled,
713
806
  });
714
807
  }
715
808
  /**
@@ -727,32 +820,50 @@ export class McpServer {
727
820
  }
728
821
  this.#resources.set(uri, resource);
729
822
  }
823
+
730
824
  /**
731
- * @param {{ name: string; description: string; title?: string; uri: string }} options
825
+ * Add a resource to the server. Resources are added manually to the context by the user to provide the LLM with additional context.
826
+ * Use the description and title to help the user to understand what the resource is.
827
+ * @param {{ name: string; description: string; title?: string; uri: string, enabled?: ()=>boolean | Promise<boolean>; }} options
732
828
  * @param {(uri: string) => Promise<ReadResourceResult> | ReadResourceResult} execute
733
829
  */
734
- resource({ name, description, title, uri }, execute) {
830
+ resource({ name, description, title, uri, enabled }, execute) {
735
831
  this.#resource({
736
832
  name,
737
833
  description,
738
834
  title,
739
835
  uri,
740
836
  execute,
837
+ enabled,
741
838
  template: false,
742
839
  });
743
840
  }
744
841
  /**
842
+ * Add a resource template to the server. Resources are added manually to the context by the user to provide the LLM with additional context.
843
+ * Resource templates are used to create resources dynamically based on a URI template. The URI template should be a valid URI template as defined in RFC 6570.
844
+ * Resource templates can have a list method that returns a list of resources that match the template and a complete method that returns a list of resources given one of the template variables, this method will
845
+ * be invoked to provide completions for the template variables to the user.
846
+ * Use the description and title to help the user to understand what the resource is.
745
847
  * @template {string} TUri
746
848
  * @template {ExtractURITemplateVariables<TUri>} TVariables
747
- * @param {{ name: string; description: string; title?: string; uri: TUri; complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>>; list?: () => Promise<Array<Resource>> | Array<Resource> }} options
849
+ * @param {{ name: string; description: string; title?: string; enabled?: ()=>boolean | Promise<boolean>; uri: TUri; complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>>; list?: () => Promise<Array<Resource>> | Array<Resource> }} options
748
850
  * @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<ReadResourceResult> | ReadResourceResult} execute
749
851
  */
750
852
  template(
751
- { name, description, title, uri, complete, list: list_resources },
853
+ {
854
+ name,
855
+ description,
856
+ title,
857
+ uri,
858
+ complete,
859
+ list: list_resources,
860
+ enabled,
861
+ },
752
862
  execute,
753
863
  ) {
754
864
  this.#resource({
755
865
  name,
866
+ enabled,
756
867
  description,
757
868
  title,
758
869
  uri,
@@ -763,6 +874,8 @@ export class McpServer {
763
874
  });
764
875
  }
765
876
  /**
877
+ * The main function that receive a JSONRpc message and either dispatch a `send` event or process the request.
878
+ *
766
879
  * @param {JSONRPCResponse | JSONRPCRequest} message
767
880
  * @param {Context} [ctx]
768
881
  * @returns {ReturnType<JSONRPCServer['receive']> | ReturnType<JSONRPCClient['receive'] | undefined>}
@@ -788,8 +901,11 @@ export class McpServer {
788
901
  this.#options.logging?.default ?? 'info',
789
902
  );
790
903
  }
904
+ const progress_token = /** @type {string | undefined} */ (
905
+ validated_message.output.params?._meta?.progressToken
906
+ );
791
907
  return this.#ctx_storage.run(
792
- ctx ?? {},
908
+ { ...(ctx ?? {}), progress_token },
793
909
  async () =>
794
910
  await this.#server.receive(validated_message.output),
795
911
  );
@@ -833,6 +949,12 @@ export class McpServer {
833
949
  }
834
950
 
835
951
  /**
952
+ * Emit an elicitation request to the client. Elicitations are used to ask the user for input in a structured way, the client will show a UI to the user to fill the input.
953
+ * The schema should be a valid Standard Schema V1 schema and should be an Object with the properties you need.
954
+ * The client will return the validated input as a JSON object that matches the schema.
955
+ *
956
+ * If the client doesn't support elicitation, it will throw an error.
957
+ *
836
958
  * @template {StandardSchema} TSchema
837
959
  * @param {TSchema} schema
838
960
  * @returns {Promise<StandardSchemaV1.InferOutput<TSchema>>}
@@ -894,8 +1016,33 @@ export class McpServer {
894
1016
  return v.parse(CreateMessageResultSchema, response);
895
1017
  }
896
1018
 
1019
+ /**
1020
+ * Send a progress notification to the client. This is useful for long-running operations where you want to inform the user about the progress.
1021
+ *
1022
+ * @param {number} progress The current progress value, it should be between 0 and total and should always increase
1023
+ * @param {number} [total] The total value, defaults to 1
1024
+ * @param {string} [message] An optional message to accompany the progress update
1025
+ */
1026
+ progress(progress, total = 1, message = undefined) {
1027
+ if (this.#progress_token != null) {
1028
+ this.#notify(
1029
+ 'notifications/progress',
1030
+ {
1031
+ progress,
1032
+ total,
1033
+ message,
1034
+ progressToken: this.#progress_token,
1035
+ },
1036
+ {
1037
+ sessions: this.#session_id ? [this.#session_id] : undefined,
1038
+ },
1039
+ );
1040
+ }
1041
+ }
1042
+
897
1043
  /**
898
1044
  * Log a message to the client if logging is enabled and the level is appropriate
1045
+ *
899
1046
  * @param {LoggingLevel} level
900
1047
  * @param {unknown} data
901
1048
  * @param {string} [logger]
@@ -19,6 +19,7 @@ export type Tool<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>, TOutp
19
19
  outputSchema?: TOutputSchema;
20
20
  title?: string;
21
21
  annotations?: ToolAnnotations;
22
+ enabled?: () => boolean | Promise<boolean>;
22
23
  execute: (
23
24
  input?: StandardSchemaV1.InferInput<TSchema>,
24
25
  ) => Promise<CallToolResult> | CallToolResult;
@@ -33,6 +34,7 @@ export type Prompt<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
33
34
  description: string;
34
35
  schema: TSchema;
35
36
  title?: string;
37
+ enabled?: () => boolean | Promise<boolean>;
36
38
  execute: (
37
39
  input?: StandardSchemaV1.InferInput<TSchema>,
38
40
  ) => Promise<GetPromptResult> | GetPromptResult;
@@ -44,6 +46,7 @@ export type StoredResource =
44
46
  name: string;
45
47
  template: true;
46
48
  title?: string;
49
+ enabled?: () => boolean | Promise<boolean>;
47
50
  list_resources?: () =>
48
51
  | Promise<Array<Resource>>
49
52
  | Array<Resource>;
@@ -57,6 +60,7 @@ export type StoredResource =
57
60
  name: string;
58
61
  template: false;
59
62
  title?: string;
63
+ enabled?: () => boolean | Promise<boolean>;
60
64
  execute: (
61
65
  uri: string,
62
66
  ) => Promise<ReadResourceResult> | ReadResourceResult;
@@ -11,7 +11,21 @@ declare module 'tmcp' {
11
11
  uri: string;
12
12
  name?: string;
13
13
  }>;
14
+ /**
15
+ * The context of the current request, include the session ID and any auth information.
16
+ * */
14
17
  get ctx(): Context;
18
+ /**
19
+ * Get the client information (name, version, etc.) of the client that initiated the current request...useful if you want to do something different based on the client.
20
+ */
21
+ currentClientInfo(): {
22
+ version: string;
23
+ name: string;
24
+ title?: string | undefined;
25
+ } | undefined;
26
+ /**
27
+ * Get the client capabilities of the client that initiated the current request, you can use this to verify the client support something before invoking the respective method.
28
+ */
15
29
  currentClientCapabilities(): {
16
30
  experimental?: {} | undefined;
17
31
  sampling?: {} | undefined;
@@ -22,40 +36,67 @@ declare module 'tmcp' {
22
36
  } | undefined;
23
37
 
24
38
  on<TEvent extends keyof McpEvents>(event: TEvent, callback: McpEvents[TEvent], options?: AddEventListenerOptions): () => void;
25
-
26
- tool<TSchema extends StandardSchema | undefined = undefined, TOutputSchema extends StandardSchema | undefined = undefined>({ name, description, title, schema, outputSchema, annotations }: {
39
+ /**
40
+ * Add a tool to the server. If you want to receive any input you need to provide a schema. The schema needs to be a valid Standard Schema V1 schema and needs to be an Object with the properties you need,
41
+ * Use the description and title to help the LLM to understand what the tool does and when to use it. If you provide an outputSchema, you need to return a structuredContent that matches the schema.
42
+ *
43
+ * Tools will be invoked by the LLM when it thinks it needs to use them, you can use the annotations to provide additional information about the tool, like what it does, how to use it, etc.
44
+ * */
45
+ tool<TSchema extends StandardSchema | undefined = undefined, TOutputSchema extends StandardSchema | undefined = undefined>({ name, description, title, schema, outputSchema, annotations, enabled, }: {
27
46
  name: string;
28
47
  description: string;
29
48
  title?: string;
49
+ enabled?: () => boolean | Promise<boolean>;
30
50
  schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
31
51
  outputSchema?: StandardSchemaV1.InferOutput<TOutputSchema extends undefined ? never : TOutputSchema> extends Record<string, unknown> ? TOutputSchema : never;
32
52
  annotations?: ToolAnnotations;
33
53
  }, execute: TSchema extends undefined ? (() => Promise<CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>> | CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>> | CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>)): void;
34
-
35
- prompt<TSchema extends StandardSchema | undefined = undefined>({ name, description, title, schema, complete }: {
54
+ /**
55
+ * Add a prompt to the server. Prompts are used to provide the user with pre-defined messages that adds context to the LLM.
56
+ * Use the description and title to help the user to understand what the prompt does and when to use it.
57
+ *
58
+ * A prompt can also have a schema that defines the input it expects, the user will be prompted to enter the inputs you request. It can also have a complete function
59
+ * for each input that will be used to provide completions for the user.
60
+ * */
61
+ prompt<TSchema extends StandardSchema | undefined = undefined>({ name, description, title, schema, complete, enabled }: {
36
62
  name: string;
37
63
  description: string;
38
64
  title?: string;
65
+ enabled?: () => boolean | Promise<boolean>;
39
66
  schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
40
67
  complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>, Completion>>>;
41
68
  }, execute: TSchema extends undefined ? (() => Promise<GetPromptResult> | GetPromptResult) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<GetPromptResult> | GetPromptResult): void;
42
-
43
- resource({ name, description, title, uri }: {
69
+ /**
70
+ * Add a resource to the server. Resources are added manually to the context by the user to provide the LLM with additional context.
71
+ * Use the description and title to help the user to understand what the resource is.
72
+ * */
73
+ resource({ name, description, title, uri, enabled }: {
44
74
  name: string;
45
75
  description: string;
46
76
  title?: string;
47
77
  uri: string;
78
+ enabled?: () => boolean | Promise<boolean>;
48
79
  }, execute: (uri: string) => Promise<ReadResourceResult> | ReadResourceResult): void;
49
-
50
- template<TUri extends string, TVariables extends ExtractURITemplateVariables<TUri>>({ name, description, title, uri, complete, list: list_resources }: {
80
+ /**
81
+ * Add a resource template to the server. Resources are added manually to the context by the user to provide the LLM with additional context.
82
+ * Resource templates are used to create resources dynamically based on a URI template. The URI template should be a valid URI template as defined in RFC 6570.
83
+ * Resource templates can have a list method that returns a list of resources that match the template and a complete method that returns a list of resources given one of the template variables, this method will
84
+ * be invoked to provide completions for the template variables to the user.
85
+ * Use the description and title to help the user to understand what the resource is.
86
+ * */
87
+ template<TUri extends string, TVariables extends ExtractURITemplateVariables<TUri>>({ name, description, title, uri, complete, list: list_resources, enabled, }: {
51
88
  name: string;
52
89
  description: string;
53
90
  title?: string;
91
+ enabled?: () => boolean | Promise<boolean>;
54
92
  uri: TUri;
55
93
  complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>>;
56
94
  list?: () => Promise<Array<Resource>> | Array<Resource>;
57
95
  }, execute: (uri: string, params: Record<TVariables, string | string[]>) => Promise<ReadResourceResult> | ReadResourceResult): void;
58
-
96
+ /**
97
+ * The main function that receive a JSONRpc message and either dispatch a `send` event or process the request.
98
+ *
99
+ * */
59
100
  receive(message: JSONRPCResponse | JSONRPCRequest, ctx?: Context): ReturnType<JSONRPCServer["receive"]> | ReturnType<JSONRPCClient["receive"] | undefined>;
60
101
  /**
61
102
  * Send a notification for subscriptions
@@ -65,14 +106,30 @@ declare module 'tmcp' {
65
106
  * Refresh roots list from client
66
107
  */
67
108
  refreshRoots(): Promise<void>;
68
-
109
+ /**
110
+ * Emit an elicitation request to the client. Elicitations are used to ask the user for input in a structured way, the client will show a UI to the user to fill the input.
111
+ * The schema should be a valid Standard Schema V1 schema and should be an Object with the properties you need.
112
+ * The client will return the validated input as a JSON object that matches the schema.
113
+ *
114
+ * If the client doesn't support elicitation, it will throw an error.
115
+ *
116
+ * */
69
117
  elicitation<TSchema extends StandardSchema>(schema: TSchema): Promise<StandardSchemaV1.InferOutput<TSchema>>;
70
118
  /**
71
119
  * Request language model sampling from the client
72
120
  * */
73
121
  message(request: CreateMessageRequestParams): Promise<CreateMessageResult>;
122
+ /**
123
+ * Send a progress notification to the client. This is useful for long-running operations where you want to inform the user about the progress.
124
+ *
125
+ * @param progress The current progress value, it should be between 0 and total and should always increase
126
+ * @param total The total value, defaults to 1
127
+ * @param message An optional message to accompany the progress update
128
+ */
129
+ progress(progress: number, total?: number, message?: string): void;
74
130
  /**
75
131
  * Log a message to the client if logging is enabled and the level is appropriate
132
+ *
76
133
  *
77
134
  */
78
135
  log(level: LoggingLevel, data: unknown, logger?: string): void;
@@ -55,6 +55,6 @@
55
55
  null,
56
56
  null
57
57
  ],
58
- "mappings": ";;;;;cAsFaA,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aCi3CsCC,kBAAkBA;MC76ClEC,UAAUA;;;;;MAsCVC,aAAaA;;;;;;;;;;;;;MAabC,UAAUA;;;;;;MAMVC,iBAAiBA;;MAEjBC,SAASA;;;;;;;;;;;;ODeRC,qBAAqBA;;;;;;;;;;;;;;OAuGrBC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;OA6BxBC,6BAA6BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuB7BC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmMxBC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4GdC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8PxBC,qBAAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BrBC,qBAAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2GrBC,oBAAoBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoEpBC,kBAAkBA;OAuGlBC,gCAAgCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8ChCC,yBAAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+LzBC,oBAAoBA;;;;;;;;;;;;;;;;;;;;;;MA8H2BC,kBAAkBA;MASbC,uBAAuBA;;;;;;MAO5BC,kBAAkBA;MAGrBC,eAAeA;MAGhBC,cAAcA;MAGFC,0BAA0BA;MAGjCC,mBAAmBA;MAY9BC,QAAQA;MAMDC,eAAeA;MAGlBC,YAAYA;MAGTC,eAAeA;;ME7/CnEC,IAAIA;;;;;;;MAOJC,cAAcA;;;;;;;MAOdC,cAAcA;;;;;MAKdC,qBAAqBA;;;;;;;;;;;;;;;;;MAiBrBC,4BAA4BA;;;;;;MAMrBC,2BAA2BA;;;;;;;OCjC1BC,iBAAiBA;;;;;;;;;;;;;;;;cAAjBA,iBAAiBA",
58
+ "mappings": ";;;;;cAiGaA,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aCs2CsCC,kBAAkBA;MC56ClEC,UAAUA;;;;;MAyCVC,aAAaA;;;;;;;;;;;;;MAabC,UAAUA;;;;;;MAMVC,iBAAiBA;;MAEjBC,SAASA;;;;;;;;;;;;ODWRC,qBAAqBA;;;;;;;;;;;;;;OAuGrBC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;OA6BxBC,6BAA6BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuB7BC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmMxBC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4GdC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8PxBC,qBAAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BrBC,qBAAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2GrBC,oBAAoBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoEpBC,kBAAkBA;OAuGlBC,gCAAgCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8ChCC,yBAAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+LzBC,oBAAoBA;;;;;;;;;;;;;;;;;;;;;;MA8H2BC,kBAAkBA;MASbC,uBAAuBA;;;;;;MAO5BC,kBAAkBA;MAGrBC,eAAeA;MAGhBC,cAAcA;MAGFC,0BAA0BA;MAGjCC,mBAAmBA;MAY9BC,QAAQA;MAMDC,eAAeA;MAGlBC,YAAYA;MAGTC,eAAeA;;ME7/CnEC,IAAIA;;;;;;;MAOJC,cAAcA;;;;;;;MAOdC,cAAcA;;;;;MAKdC,qBAAqBA;;;;;;;;;;;;;;;;;MAiBrBC,4BAA4BA;;;;;;MAMrBC,2BAA2BA;;;;;;;OCjC1BC,iBAAiBA;;;;;;;;;;;;;;;;cAAjBA,iBAAiBA",
59
59
  "ignoreList": []
60
60
  }