Introducing Code Mode and MCP Tool Transforms in NitroStack 1.1

Monolithic tool catalogs waste tokens and slow down agent loops. NitroStack 1.1 introduces an opt-in MCP transform pipeline, sandboxed QuickJS Code Mode, and progressive discovery to streamline agentic workflows.

Hemant Jadhav
Hemant Jadhav
NitroStack 1.1 Code Mode and MCP Tool Transforms hero banner with 3D isometric pipeline cubes and blueprint grid

When servers expose dozens of tools over the Model Context Protocol (MCP), a fundamental friction point emerges: monolithic tool catalogs degrade model performance.

Under the traditional MCP model, a server emits every available tool definition in response to tools/list. As enterprise services connect databases, internal APIs, and administration scripts, catalogs quickly balloon past 50 to 100 tools. This flood of JSON schemas causes three critical bottlenecks:

  1. Severe Prompt Bloat: Tool definitions consume 8,000 to 20,000 tokens before the user even types a single word, burning API budgets and crowding out working memory.
  2. Multi-Hop Round-Trip Latency: When an autonomous agent needs to inspect a dataset, filter results, and invoke subsequent tools, it must execute multiple LLM inference loops across the network.
  3. Context Window Blowups: A single database query or search tool returning 50 KB of JSON can overflow the agent's context window or trigger prompt truncation.

With the release of NitroStack 1.1 (specifically @nitrostack/core and @nitrostack/cli 1.1.0), we are introducing an extensible MCP Tool Transform Pipeline. This architecture enables servers to filter, search, batch-execute tools in sandboxed WebAssembly environments, and render interactive UI widgets without altering a single line of underlying business logic.

text
┌─────────────────────────────────────────────────────────────────────────────┐
│                 NITROSTACK 1.1 MCP TOOL TRANSFORM PIPELINE                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Client Request (tools/list, tools/call)                                   │
│                           │                                                 │
│                           ▼                                                 │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │ 1. VisibilityTransform                                              │   │
│   │    Enforces session-scoped tool visibility via SessionVisibilityStore│  │
│   └───────────────────────┬─────────────────────────────────────────────┘   │
│                           │                                                 │
│         ┌─────────────────┴─────────────────┐                               │
│         ▼                                   ▼                               │
│   ┌───────────────────────────┐       ┌───────────────────────────────┐     │
│   │ 2A. BM25 / Regex Search   │       │ 2B. CodeModeTransform         │     │
│   │     Progressive Discovery │       │     Sandboxed QuickJS WASM    │     │
│   │     Prunes schemas by >80%│       │     Batches multi-tool calls  │     │
│   └─────────────┬─────────────┘       └───────────────┬───────────────┘     │
│                 │                                     │                     │
│                 └─────────────────┬───────────────────┘                     │
│                                   ▼                                         │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │ 3. DataSpilloverInterceptor                                         │   │
│   │    Offloads >10KB tool outputs to resource://data-spillover/{id}    │   │
│   └───────────────────────┬─────────────────────────────────────────────┘   │
│                           │                                                 │
│                           ▼                                                 │
│   Native Business Tools (@Tool controllers) & Interactive Next.js Widgets   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

1. The MCP Transform Pipeline Architecture

At the center of NitroStack 1.1 is the McpTransform abstraction. Rather than treating tool registration as an immutable array, transforms act as intelligent middleware that sits between the MCP transport layer and your application controllers.

Transforms intercept two primary protocol touchpoints:

  • transformTools(tools, ctx): Intercepts the catalog emitted during tools/list and reshapes the tools exposed to the agent.
  • transformCall(call, next, ctx): Intercepts tools/call invocations, allowing custom validation, routing, sandboxing, or multi-tool batch execution.

Here is how you enable transforms in your main application:

typescript
import { McpApp, Module, CodeModeTransform, VisibilityTransform } from '@nitrostack/core';
import { AnalyticsController } from './analytics.controller';
import { DatabaseController } from './database.controller';

@McpApp({
  module: AppModule,
  server: { 
    name: 'enterprise-data-service', 
    version: '1.1.0' 
  },
  transforms: [
    new VisibilityTransform(),
    new CodeModeTransform({
      timeoutMs: 15000,
      memoryLimitMb: 128,
      workerPoolSize: 4,
    }),
  ],
})
@Module({
  name: 'app',
  controllers: [AnalyticsController, DatabaseController],
})
export class AppModule {}

Solving Infinite Recursion with AsyncLocalStorage

When a transform intercepts a high-level request, it frequently needs to invoke the underlying business tools directly. In naive architectures, this triggers an infinite recursion loop: the internal call re-enters the transform pipeline, which intercepts itself.

NitroStack solves this with an internal AsyncLocalStorage bypass (catalog.transform.ts). When an execution originates from within a transform, the runner marks the asynchronous context with a pipeline bypass flag. The underlying controller runs at bare-metal speed while the external client sees only the transformed surface.


2. Sandboxed Code Mode: Eliminating Round-Trip Latency

Traditional agentic tool use is conversational. If an agent needs to retrieve 1,000 customer records, filter for active subscriptions, and calculate churn risk, it executes three distinct turns:

  1. LLM requests get_customers. Server returns 1,000 items over the wire.
  2. LLM inspects the payload and requests filter_subscriptions.
  3. LLM invokes calculate_churn.

This sequential model wastes time and consumes enormous inference bandwidth.

Code Mode changes this paradigm. When CodeModeTransform is enabled, NitroStack synthesizes three meta-tools:

  • search: Allows the agent to find relevant tools by keyword or regex.
  • get_schema: Returns the precise parameter definition for a specific tool.
  • execute: Runs a client-authored JavaScript snippet inside a dedicated QuickJS WebAssembly sandbox.

Inside the sandbox, the agent writes programmatic JavaScript that orchestrates tools directly on the server:

javascript
// Script generated by LLM and executed inside QuickJS sandbox
const users = await callTool('get_customers', { limit: 1000 });
const activeUsers = users.filter(u => u.status === 'active');
const analytics = await callTool('calculate_churn', { userIds: activeUsers.map(u => u.id) });

return {
  totalAnalyzed: activeUsers.length,
  highRiskCount: analytics.highRisk.length,
  summary: analytics.metrics
};

Instead of sending megabytes of intermediate records back and forth to the model, all intermediate data processing happens locally inside the WebAssembly runtime. The LLM receives only the final computed summary in a single turn.

Hardened WebAssembly Sandbox

Because the server executes untrusted code generated by an LLM, security and stability are non-negotiable:

  • Strict Memory Ceilings: Every QuickJS worker thread is bounded by hard memory caps (such as 128 MB).
  • Watchdog Execution Timeouts: Scripts that enter infinite loops are forcefully terminated after a configurable threshold (e.g., 15,000 ms).
  • Heap Growth Containment: Workers are instrumented with heap checks to ensure recycled threads do not leak memory across multi-tenant sessions.

3. Progressive Tool Discovery with BM25 Indexing

For servers that do not require arbitrary code execution, BM25SearchTransform solves the catalog bloat problem through progressive discovery.

Instead of registering all 80 tools in the system prompt, NitroStack collapses the catalog down to two primary endpoints:

  • search_tools: Searches tool definitions using Okapi BM25 relevance scoring or regular expressions.
  • call_tool: Invokes any discovered tool by name.
text
┌─────────────────────────────────────────────────────────────────────────────┐
│                 CATALOG TOKEN FOOTPRINT COMPARISON                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│ Standard MCP (Monolithic 60 Tools):                                         │
│ [████████████████████████████████████████████████████████████] ~14,200 toks │
│                                                                             │
│ NitroStack Progressive Discovery (search_tools + call_tool):                │
│ [████████] ~1,650 toks (>85% prompt token reduction)                        │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Crucially, NitroStack supports Catalog Browsing Mode. If an LLM calls search_tools with an omitted or empty query, the server returns a paginated listing of available tool categories, allowing the agent to explore the server's capabilities organically without guessing keywords.

You can also designate critical tools as alwaysVisible: true, ensuring high-frequency operations remain directly accessible in the root catalog for zero-latency execution.


4. Large Payload Spillover Protection

When tools return large responses, LLM context windows frequently crash. A standard SQL query or log extraction can easily generate 150 KB of output, instantly blowing past context limits.

NitroStack 1.1 introduces the DataSpilloverInterceptor. When decorated on a controller method or registered globally, any tool response exceeding a configurable size threshold (such as 10 KB) is automatically intercepted:

typescript
import { Controller, Tool, UseInterceptors } from '@nitrostack/core';
import { DataSpilloverInterceptor } from '@nitrostack/core/interceptors';

@Controller()
export class ReportController {
  @Tool({ name: 'export_audit_logs', description: 'Exports system audit entries' })
  @UseInterceptors(new DataSpilloverInterceptor({ maxBytes: 10240 }))
  async exportLogs(input: { timeframe: string }) {
    return this.auditService.fetchLogs(input.timeframe); // Returns 2 MB payload
  }
}

When this tool runs, the interceptor:

  1. Persists the complete, unmodified payload into an MCP resource store (MemorySpilloverStore or FsSpilloverStore).
  2. Assigns a deterministic resource URI: resource://data-spillover/{spilloverId}.
  3. Automatically synthesizes a compact preview for the LLM containing the schema shape, total row count, sample records, and the resource URI.

The agent stays within its token budget and can read specific slices of the data using the standard resources/read protocol method whenever necessary.


5. Interactive UI Widgets with Next.js

Tool execution should not be limited to raw JSON text. In modern AI applications, users expect visual charts, interactive tables, and diagnostic widgets.

NitroStack 1.1 includes bundled CLI presets and Next.js widget preview templates:

bash
npx @nitrostack/cli@1.1.0 init my-agent-service --preset code-mode

Each generated project includes a dedicated widgets directory with Next.js components. During build time, widgets compile into self-contained, standalone HTML and web component bundles. When tools execute, they return structured widget manifests (widget-manifest.json) that modern AI clients (like ChatGPT, Cursor, and NitroChat) render directly in the user interface.

text
┌─────────────────────────────────────────────────────────────────────────────┐
│                    INTERACTIVE WIDGET COMPILATION FLOW                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   src/widgets/system-health.tsx  (Next.js Component)                        │
│                   │                                                         │
│                   ▼  (Build step: next build && static export)              │
│   out/system-health.html         (Self-contained HTML + Tailwind CSS)       │
│                   │                                                         │
│                   ▼                                                         │
│   @Tool returns: { result: data, widget: "system-health" }                  │
│                   │                                                         │
│                   ▼                                                         │
│   AI Client renders interactive diagnostic dashboard directly in chat       │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

6. Migration and Backwards Compatibility

A major priority in the 1.1 release was zero breaking changes. All transform pipelines, code mode execution environments, and discovery mechanics are strictly opt-in.

If you run an existing NitroStack server on stdio, SSE, or stateless HTTP without configuring transforms: [...], your server continues to function with zero changes. Core test coverage across @nitrostack/core and @nitrostack/cli currently stands at 101 test suites and 1,189 passing tests.

Getting Started with 1.1.0

To upgrade your existing project or scaffold a new service:

bash
# Upgrade core packages
npm install @nitrostack/core@1.1.0 @nitrostack/cli@1.1.0

# Or scaffold a fresh Code Mode server
npx @nitrostack/cli@1.1.0 init my-code-server --preset code-mode

In upcoming deep dives, we will break down the systems engineering behind our QuickJS WebAssembly worker pool, benchmark BM25 token savings against monolithic catalogs, and walk through implementing session-scoped tool visibility for multi-tenant applications.

About the Author

Hemant Jadhav

Hemant Jadhav

Author