Core Concepts

Bootstrap

Create and start the server, and configure it with @McpApp

Every NitroStack server starts the same way: src/index.ts creates the application from your root module and starts it. This page covers that entry point, the options start() accepts, and the @McpApp decorator that configures the server.

Prerequisites

NitroStack relies on TypeScript decorator metadata, so it needs reflect-metadata and two compiler options. Projects created with nitrostack-cli init already have both. Install the packages:

npm install @nitrostack/core reflect-metadata zod

Then turn on decorator metadata in tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "target": "ES2022"
  }
}

Application entry point

The entry file creates the application from your root module, then starts it. Options passed to start() override the ones in @McpApp.

Important

import 'reflect-metadata' must be the very first import in your entry file (src/index.ts). Without it, TypeScript decorators cannot persist runtime metadata.

// src/index.ts
import 'reflect-metadata'; // Must be first
import { McpApplicationFactory } from '@nitrostack/core';
import { AppModule } from './app.module.js';

async function bootstrap() {
  const server = await McpApplicationFactory.create(AppModule);
  
  // Starts with defaults from @McpApp, or pass programmatic overrides:
  await server.start({
    transport: 'dual', // 'stdio' | 'http' | 'dual'
    port: 8080,        // Custom HTTP port
    host: '0.0.0.0',   // Custom host binding
    endpoint: '/mcp',  // MCP base path (default: '/mcp')
    enableCors: true,  // CORS headers for browser clients
  });
}

bootstrap();

ServerStartOptions

When calling server.start(options), the following options can override @McpApp decorator defaults:

PropertyTypeDefaultDescription
transport'stdio' | 'http' | 'dual'Value from @McpAppTransport listener mode.
portnumber3000Port for HTTP listeners.
hoststringHOST env var, else 'localhost'Host interface binding. Use '0.0.0.0' to accept connections from other machines or containers.
endpointstring'/mcp'Base path for streamable MCP routes.
enableCorsbooleantrueEnables Cross-Origin Resource Sharing for browser web clients.

The @McpApp decorator

The @McpApp decorator configures your root application module, transport modes, and server metadata:

// src/app.module.ts
import {
  McpApp,
  Module,
  ConfigModule,
  BM25SearchTransform,
} from '@nitrostack/core';
import { ProductsModule } from './modules/products/products.module.js';

@McpApp({
  module: AppModule,
  server: {
    name: 'bookshelf-server',
    version: '1.0.0',
    protocolVersion: 'auto', // 'auto' (default: 2026-07-28 + legacy fallback) | '2026-07-28' | 'legacy'
  },
  logging: {
    level: 'info', // 'debug' | 'info' | 'warn' | 'error'
  },
  transport: {
    type: 'dual', // 'stdio' | 'http' | 'dual'
    http: {
      port: 3000,
      host: '0.0.0.0',
      basePath: '/mcp',
    },
  },
  shutdownHooks: true, // Graceful SIGINT/SIGTERM lifecycle execution
  transforms: [
    // Progressive tool discovery pipeline to minimize LLM token consumption
    new BM25SearchTransform({
      defaultLimit: 5,
      alwaysVisible: ['products_search_catalog'],
    }),
  ],
})
@Module({
  imports: [
    ConfigModule.forRoot(),
    ProductsModule,
  ],
})
export class AppModule {}

Protocol version

The protocolVersion option chooses which revision of MCP the HTTP endpoint speaks.

ValueWhat it serves
auto (default)The stateless 2026-07-28 protocol, with a fallback for older sessionful clients
2026-07-28Only the stateless protocol: server/discover, a per-request _meta envelope, and multi round-trip requests via inputRequired
legacy or 2025-06-18Only the sessionful Streamable HTTP protocol, with an Mcp-Session-Id header

Tip

The environment variable NITRO_MCP_PROTOCOL_VERSION takes precedence over @McpApp({ server: { protocolVersion } }), allowing DevOps operators to switch deployed protocol modes without modifying code.

What create() and start() do

McpApplicationFactory.create() reads the @McpApp metadata from your root module, sets up logging and the dependency injection container, registers every imported module, and builds your tools, resources, and prompts. start() then runs the startup lifecycle hooks and opens the transports.

1Read @McpAppRoot module metadata
2Set up logging and DIContainer and logger
3Register modulesImports, recursively
4Build primitivesTools, resources, prompts
5Start transportsSTDIO, HTTP, or both
What happens between create() and the first request.

Spillover storage

To prevent oversized tool outputs from overflowing model context windows and exhausting Node.js heap memory, servers can configure default spillover storage:

import { createServer } from '@nitrostack/core';

// Production filesystem configuration:
const server = createServer({
  name: 'enterprise-server',
  version: '1.0.0',
  spillover: {
    driver: 'filesystem',
    storageDir: './.spillover_cache',
    maxSizeBytes: 200 * 1024 * 1024, // 200MB maximum disk quota
  },
});

// Development in-memory configuration:
const devServer = createServer({
  name: 'dev-server',
  version: '1.0.0',
  spillover: {
    driver: 'memory',
    maxSizeBytes: 20 * 1024 * 1024,  // 20MB in-memory buffer
  },
});

When configured, NitroStack automatically exposes the built-in resource://data-spillover/{id} endpoint, allowing AI models to inspect spilled datasets on demand via standard MCP resources/read.

Next steps

On this page