TypeScriptTransforms

Progressive Search

Replace the tool catalog with search_tools and call_tool (BM25SearchTransform)

BM25SearchTransform hides the full catalog. The client sees two tools, search_tools and call_tool, plus any tools you mark as always visible. The model searches for what it needs, then calls it by name.

The BM25SearchTransform indexes all registered tools using an in-memory BM25 information retrieval engine. When a client requests tools/list, it returns two synthetic tools instead of the entire catalog:

  • search_tools: Performs keyword or natural-language relevance searches against indexed tool names, descriptions, and parameter definitions.
  • call_tool: Dispatches an invocation to any underlying tool returned by the search query.

Configuration

Add the transform to @McpApp. Put tools the model needs constantly, such as sign-in, in alwaysVisible so they skip the search step.

import { McpApp, Module, BM25SearchTransform } from '@nitrostack/core';
import { FinanceController } from './finance.controller.js';
import { InventoryController } from './inventory.controller.js';

@McpApp({
  module: AppModule,
  server: {
    name: 'enterprise-inventory-service',
    version: '1.0.0',
  },
  transforms: [
    new BM25SearchTransform({
      defaultLimit: 5,
      defaultDetail: 'detailed', // 'brief' | 'detailed' | 'full'
      alwaysVisible: [
        'finance_auth_login',
        'inventory_get_status',
      ],
      searchToolDescription:
        'Searches available tools by keywords or natural language task description. ' +
        'Query to retrieve matching tool names and schemas before calling them.',
    }),
  ],
})
@Module({
  controllers: [FinanceController, InventoryController],
})
export class AppModule {}

Options

Every option is optional.

OptionTypeDefaultDescription
defaultLimitnumber5Maximum number of search results returned per query.
defaultDetail'brief' | 'detailed' | 'full''detailed'Schema detail level returned by search_tools.
alwaysVisiblestring[][]List of tool names that bypass catalog collapsing and remain directly visible.
searchToolNamestring'search_tools'Custom name for the synthetic search tool.
callToolNamestring'call_tool'Custom name for the synthetic dispatch tool.
searchToolDescriptionstringBuilt-inCustom prompt description for search_tools.
callToolDescriptionstringBuilt-inCustom prompt description for call_tool.
allowRegexbooleanfalseEnable regular expression pattern matching (RegexSearchTransform).

What the model does

A conversation with a search transform has three steps: list the catalog, search it, then call the tool the search found.

Model
NitroStack server
1tools/list
2search_tools, call_toolplus inventory_get_status, which is always visible
3search_tools{ query: "transfer" }
4finance_transfer_fundswith its description and input schema
5call_tool{ name: "finance_transfer_funds", arguments: { to: "123", amount: 500 } }
Validates the arguments against the tool schema, then runs it
6Result{ transferId: 88 }
The model only sees the full schema of a tool after searching for it.

When routed through call_tool, NitroStack automatically validates incoming arguments against the underlying tool's Zod schema before execution.

Next steps

On this page