@ttoss/http-server-mcp 0.34.0 → 0.34.1

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
@@ -484,14 +484,14 @@ registerTools({ server: fullServer, tools });
484
484
  registerTools({ server: deferredServer, tools, defer: true });
485
485
  ```
486
486
 
487
- A schema enters the model's context only for the tools it is about to use. `call` enforces the tool's schema as a direct call would, and its errors help the model recover: an unknown name answers suggestions, and invalid arguments, a tool's own error result, or a thrown error carry the input schema, so a `call` made without `describe` corrects itself in one retry.
487
+ A schema enters the model's context only for the tools it is about to use. `call` enforces the tool's schema as a direct call would, and its errors help the model recover: an unknown name answers suggestions (near-miss names, then tools the name's words match; a lone prefix in a description is not enough), and invalid arguments, a tool's own error result, or a thrown error carry the input schema, so a `call` made without `describe` corrects itself in one retry.
488
488
 
489
489
  Pass options instead of `true` to tune it:
490
490
 
491
491
  - `except` — tools kept standalone, not deferred; they stay reachable through `search` too. Defaults to those linked to an [MCP Apps](#mcp-apps-interactive-uis) view, since a host finds a view through the tool's own `tools/list` entry.
492
492
  - `visible` — runs on every `search`, `describe` and `call`; a tool it rejects is absent from all three. Standalone tools stay listed.
493
493
  - `search` — replaces the default ranking, `rankTools`, e.g. with embeddings.
494
- - `searchLimit` (default `10`) and `names` (default `search` / `describe` / `call`).
494
+ - `searchLimit` (default `10`), `searchMaxLimit` — the largest `limit` accepted (default `50`, or `searchLimit` when higher) — and `names` (default `search` / `describe` / `call`).
495
495
 
496
496
  A gate (below) sees each call with the tool's own name and arguments, so authorization stays per tool when deferred.
497
497
 
package/dist/index.cjs CHANGED
@@ -507,6 +507,7 @@ var defineTool = ({
507
507
  //#region src/toolSearch.ts
508
508
  var SUMMARY_MAX_LENGTH = 160;
509
509
  var SUGGESTION_COUNT = 3;
510
+ var MIN_RELATED_SCORE = 1;
510
511
  /** Plural and singular forms rank alike: `agents` finds `get-agent`. */
511
512
  var stem = term => {
512
513
  if (term.length > 4 && term.endsWith("ies")) return `${term.slice(0, -3)}y`;
@@ -573,23 +574,29 @@ var scoreOf = ({
573
574
  }
574
575
  return score;
575
576
  };
577
+ var withTag = ({
578
+ tag,
579
+ tools
580
+ }) => {
581
+ return tag === void 0 ? tools : tools.filter(tool => {
582
+ return tool.tags?.includes(tag) ?? false;
583
+ });
584
+ };
576
585
  /**
577
- * The deferred tools' default search: scores each tool by the query's terms,
578
- * weighted name > summary > tags > description, and breaks ties by the
579
- * tools' order. No index — a few hundred tools need none.
586
+ * The tools `query` matches, best first, ties in the tools' order; `undefined`
587
+ * when the query has no words.
580
588
  */
581
- var rankTools = ({
589
+ var scoreTools = ({
582
590
  query,
583
591
  tag,
584
- limit,
585
592
  tools
586
593
  }) => {
587
- const candidates = tag === void 0 ? tools : tools.filter(tool => {
588
- return tool.tags?.includes(tag) ?? false;
589
- });
590
594
  const terms = [...new Set(termsOf(query))];
591
- if (terms.length === 0) return candidates.slice(0, limit);
592
- return candidates.map((tool, index) => {
595
+ if (terms.length === 0) return void 0;
596
+ return withTag({
597
+ tag,
598
+ tools
599
+ }).map((tool, index) => {
593
600
  return {
594
601
  tool,
595
602
  index,
@@ -604,7 +611,29 @@ var rankTools = ({
604
611
  return score > 0;
605
612
  }).sort((a, b) => {
606
613
  return b.score - a.score || a.index - b.index;
607
- }).slice(0, limit).map(({
614
+ });
615
+ };
616
+ /**
617
+ * The deferred tools' default search: scores each tool by the query's terms,
618
+ * weighted name > summary > tags > description, and breaks ties by the
619
+ * tools' order. No index — a few hundred tools need none.
620
+ */
621
+ var rankTools = ({
622
+ query,
623
+ tag,
624
+ limit,
625
+ tools
626
+ }) => {
627
+ const scored = scoreTools({
628
+ query,
629
+ tag,
630
+ tools
631
+ });
632
+ if (!scored) return withTag({
633
+ tag,
634
+ tools
635
+ }).slice(0, limit);
636
+ return scored.slice(0, limit).map(({
608
637
  tool
609
638
  }) => {
610
639
  return tool;
@@ -651,11 +680,16 @@ var suggestionsFor = ({
651
680
  }).map(candidate => {
652
681
  return candidate.name;
653
682
  });
654
- const related = rankTools({
683
+ const related = (scoreTools({
655
684
  query: name,
656
- limit: SUGGESTION_COUNT,
657
685
  tools
658
- }).map(tool => {
686
+ }) ?? []).filter(({
687
+ score
688
+ }) => {
689
+ return score >= MIN_RELATED_SCORE;
690
+ }).slice(0, SUGGESTION_COUNT).map(({
691
+ tool
692
+ }) => {
659
693
  return tool.name;
660
694
  });
661
695
  return [...new Set([...nearMisses, ...related])].slice(0, SUGGESTION_COUNT);
@@ -664,6 +698,7 @@ var suggestionsFor = ({
664
698
  //#endregion
665
699
  //#region src/deferTools.ts
666
700
  var DEFAULT_SEARCH_LIMIT = 10;
701
+ var DEFAULT_SEARCH_MAX_LIMIT = 50;
667
702
  var jsonResult = ({
668
703
  data,
669
704
  isError = false
@@ -708,6 +743,7 @@ var deferredToolsOf = ({
708
743
  tool
709
744
  }) : true;
710
745
  };
746
+ const searchLimit = options.searchLimit ?? DEFAULT_SEARCH_LIMIT;
711
747
  return {
712
748
  names: {
713
749
  search: "search",
@@ -716,7 +752,8 @@ var deferredToolsOf = ({
716
752
  ...options.names
717
753
  },
718
754
  tools,
719
- searchLimit: options.searchLimit ?? DEFAULT_SEARCH_LIMIT,
755
+ searchLimit,
756
+ searchMaxLimit: options.searchMaxLimit ?? Math.max(DEFAULT_SEARCH_MAX_LIMIT, searchLimit),
720
757
  searchTools: options.search ?? rankTools,
721
758
  visibleTools: () => {
722
759
  return tools.filter(isVisible);
@@ -731,6 +768,7 @@ var searchToolOf = ({
731
768
  names,
732
769
  tools,
733
770
  searchLimit,
771
+ searchMaxLimit,
734
772
  searchTools,
735
773
  visibleTools
736
774
  }) => {
@@ -745,7 +783,7 @@ var searchToolOf = ({
745
783
  properties: {
746
784
  query: {
747
785
  type: "string",
748
- description: "Words describing the action, e.g. \"create agent\". Empty lists every tool."
786
+ description: "Words describing the action, e.g. \"create agent\". Empty lists the tools in order."
749
787
  },
750
788
  tag: {
751
789
  type: "string",
@@ -757,6 +795,7 @@ var searchToolOf = ({
757
795
  limit: {
758
796
  type: "integer",
759
797
  minimum: 1,
798
+ maximum: searchMaxLimit,
760
799
  description: `Maximum number of results. Defaults to ${searchLimit}.`
761
800
  }
762
801
  },
package/dist/index.d.cts CHANGED
@@ -865,6 +865,12 @@ interface DeferToolsOptions {
865
865
  * @default 10
866
866
  */
867
867
  searchLimit?: number;
868
+ /**
869
+ * The largest `limit` `search` accepts. With no ceiling, an empty query
870
+ * lists every tool, more than a client's context holds.
871
+ * @default 50, or `searchLimit` when that is higher
872
+ */
873
+ searchMaxLimit?: number;
868
874
  /**
869
875
  * Runs on every `search`, `describe` and `call`. A tool it rejects is
870
876
  * absent from all three; `call` answers it as an unknown name. Tools
package/dist/index.d.mts CHANGED
@@ -866,6 +866,12 @@ interface DeferToolsOptions {
866
866
  * @default 10
867
867
  */
868
868
  searchLimit?: number;
869
+ /**
870
+ * The largest `limit` `search` accepts. With no ceiling, an empty query
871
+ * lists every tool, more than a client's context holds.
872
+ * @default 50, or `searchLimit` when that is higher
873
+ */
874
+ searchMaxLimit?: number;
869
875
  /**
870
876
  * Runs on every `search`, `describe` and `call`. A tool it rejects is
871
877
  * absent from all three; `call` answers it as an unknown name. Tools
package/dist/index.mjs CHANGED
@@ -504,6 +504,7 @@ var defineTool = ({
504
504
  //#region src/toolSearch.ts
505
505
  var SUMMARY_MAX_LENGTH = 160;
506
506
  var SUGGESTION_COUNT = 3;
507
+ var MIN_RELATED_SCORE = 1;
507
508
  /** Plural and singular forms rank alike: `agents` finds `get-agent`. */
508
509
  var stem = term => {
509
510
  if (term.length > 4 && term.endsWith("ies")) return `${term.slice(0, -3)}y`;
@@ -570,23 +571,29 @@ var scoreOf = ({
570
571
  }
571
572
  return score;
572
573
  };
574
+ var withTag = ({
575
+ tag,
576
+ tools
577
+ }) => {
578
+ return tag === void 0 ? tools : tools.filter(tool => {
579
+ return tool.tags?.includes(tag) ?? false;
580
+ });
581
+ };
573
582
  /**
574
- * The deferred tools' default search: scores each tool by the query's terms,
575
- * weighted name > summary > tags > description, and breaks ties by the
576
- * tools' order. No index — a few hundred tools need none.
583
+ * The tools `query` matches, best first, ties in the tools' order; `undefined`
584
+ * when the query has no words.
577
585
  */
578
- var rankTools = ({
586
+ var scoreTools = ({
579
587
  query,
580
588
  tag,
581
- limit,
582
589
  tools
583
590
  }) => {
584
- const candidates = tag === void 0 ? tools : tools.filter(tool => {
585
- return tool.tags?.includes(tag) ?? false;
586
- });
587
591
  const terms = [...new Set(termsOf(query))];
588
- if (terms.length === 0) return candidates.slice(0, limit);
589
- return candidates.map((tool, index) => {
592
+ if (terms.length === 0) return void 0;
593
+ return withTag({
594
+ tag,
595
+ tools
596
+ }).map((tool, index) => {
590
597
  return {
591
598
  tool,
592
599
  index,
@@ -601,7 +608,29 @@ var rankTools = ({
601
608
  return score > 0;
602
609
  }).sort((a, b) => {
603
610
  return b.score - a.score || a.index - b.index;
604
- }).slice(0, limit).map(({
611
+ });
612
+ };
613
+ /**
614
+ * The deferred tools' default search: scores each tool by the query's terms,
615
+ * weighted name > summary > tags > description, and breaks ties by the
616
+ * tools' order. No index — a few hundred tools need none.
617
+ */
618
+ var rankTools = ({
619
+ query,
620
+ tag,
621
+ limit,
622
+ tools
623
+ }) => {
624
+ const scored = scoreTools({
625
+ query,
626
+ tag,
627
+ tools
628
+ });
629
+ if (!scored) return withTag({
630
+ tag,
631
+ tools
632
+ }).slice(0, limit);
633
+ return scored.slice(0, limit).map(({
605
634
  tool
606
635
  }) => {
607
636
  return tool;
@@ -648,11 +677,16 @@ var suggestionsFor = ({
648
677
  }).map(candidate => {
649
678
  return candidate.name;
650
679
  });
651
- const related = rankTools({
680
+ const related = (scoreTools({
652
681
  query: name,
653
- limit: SUGGESTION_COUNT,
654
682
  tools
655
- }).map(tool => {
683
+ }) ?? []).filter(({
684
+ score
685
+ }) => {
686
+ return score >= MIN_RELATED_SCORE;
687
+ }).slice(0, SUGGESTION_COUNT).map(({
688
+ tool
689
+ }) => {
656
690
  return tool.name;
657
691
  });
658
692
  return [...new Set([...nearMisses, ...related])].slice(0, SUGGESTION_COUNT);
@@ -661,6 +695,7 @@ var suggestionsFor = ({
661
695
  //#endregion
662
696
  //#region src/deferTools.ts
663
697
  var DEFAULT_SEARCH_LIMIT = 10;
698
+ var DEFAULT_SEARCH_MAX_LIMIT = 50;
664
699
  var jsonResult = ({
665
700
  data,
666
701
  isError = false
@@ -705,6 +740,7 @@ var deferredToolsOf = ({
705
740
  tool
706
741
  }) : true;
707
742
  };
743
+ const searchLimit = options.searchLimit ?? DEFAULT_SEARCH_LIMIT;
708
744
  return {
709
745
  names: {
710
746
  search: "search",
@@ -713,7 +749,8 @@ var deferredToolsOf = ({
713
749
  ...options.names
714
750
  },
715
751
  tools,
716
- searchLimit: options.searchLimit ?? DEFAULT_SEARCH_LIMIT,
752
+ searchLimit,
753
+ searchMaxLimit: options.searchMaxLimit ?? Math.max(DEFAULT_SEARCH_MAX_LIMIT, searchLimit),
717
754
  searchTools: options.search ?? rankTools,
718
755
  visibleTools: () => {
719
756
  return tools.filter(isVisible);
@@ -728,6 +765,7 @@ var searchToolOf = ({
728
765
  names,
729
766
  tools,
730
767
  searchLimit,
768
+ searchMaxLimit,
731
769
  searchTools,
732
770
  visibleTools
733
771
  }) => {
@@ -742,7 +780,7 @@ var searchToolOf = ({
742
780
  properties: {
743
781
  query: {
744
782
  type: "string",
745
- description: "Words describing the action, e.g. \"create agent\". Empty lists every tool."
783
+ description: "Words describing the action, e.g. \"create agent\". Empty lists the tools in order."
746
784
  },
747
785
  tag: {
748
786
  type: "string",
@@ -754,6 +792,7 @@ var searchToolOf = ({
754
792
  limit: {
755
793
  type: "integer",
756
794
  minimum: 1,
795
+ maximum: searchMaxLimit,
757
796
  description: `Maximum number of results. Defaults to ${searchLimit}.`
758
797
  }
759
798
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/http-server-mcp",
3
- "version": "0.34.0",
3
+ "version": "0.34.1",
4
4
  "description": "Model Context Protocol (MCP) server integration for @ttoss/http-server",
5
5
  "keywords": [
6
6
  "ai",
@@ -39,8 +39,8 @@
39
39
  "zod": "^4.4.3",
40
40
  "@ttoss/auth-core": "^0.16.1",
41
41
  "@ttoss/http-server": "^0.11.1",
42
- "@ttoss/http-server-auth": "^0.8.1",
43
- "@ttoss/i18n-core": "^0.7.0"
42
+ "@ttoss/i18n-core": "^0.7.0",
43
+ "@ttoss/http-server-auth": "^0.8.1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@modelcontextprotocol/client": "^2.0.0",