npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lensmcp/nest-instrumentation

v1.13.0

Published

Zero-config NestJS instrumentation for LensMCP — createLensmcpNestApp.

Readme

@lensmcp/nest-instrumentation

Zero-config NestJS instrumentation for LensMCP — the observability lens for coding agents.

This package wires LensMCP into a NestJS application so an agent can see how requests actually flow through it: every controller hop and provider-method call becomes a timed span, each HTTP request becomes a server-request event with method/route/status/duration, repeated DB / Redis / external calls inside one method surface as loop (N+1) warnings, and the live provider graph is materialised from singleton-instance events. All of it is correlated by a per-request flowId / requestId carried across async boundaries via AsyncLocalStorage, so the trace from an inbound request through its services, queries, and queue jobs stays connected.

There are two ways to turn it on, and you only need one:

  1. Explicit drop-in. Replace NestFactory.create(AppModule) with createLensmcpNestApp(AppModule, …) in your main.ts. One import, no edits to AppModule.
  2. Zero-touch graft. Do nothing. When @lensmcp/node-instrumentation is loaded into the process, it patches NestFactory.create and grafts LensmcpModule.forRoot around your root module automatically — no import in your source at all. This is how LensMCP instruments apps under its zero-touch runtime.

Both paths produce the same instrumentation; the drop-in is just the graft done by hand.

Install

yarn add @lensmcp/nest-instrumentation

@nestjs/common, @nestjs/core (>=10 <12), and rxjs (^7) are peer dependencies — already present in any Nest app.

Usage

In your main.ts, swap NestFactory.create for createLensmcpNestApp:

// main.ts
import { createLensmcpNestApp } from '@lensmcp/nest-instrumentation';
import { AppModule } from './app.module';

const app = await createLensmcpNestApp(AppModule, {
  projectName: 'api',
});

await app.listen(3100);

That is the whole setup. createLensmcpNestApp builds a wrapper root module that imports both LensmcpModule and your AppModule, then calls NestFactory.create for you — so AppModule is never touched. Because the wrapper lists LensMCP first, the trace interceptor is registered outermost: a user interceptor that short-circuits (e.g. a cache hit) cannot bypass tracing.

The drop-in turns on zero-config defaults: trace.autoInstrumentMethods is true and memory defaults to { mode: 'light', scope: 'all' }, so every provider's methods are traced and every provider's Map / Set / Array fields are watched for growth — with no @TraceMethod or @TraceProvider decorators required. You can override any of these:

const app = await createLensmcpNestApp(AppModule, {
  projectName: 'api',
  trace: { autoInstrumentMethods: true, db: true },
  memory: { mode: 'light', scope: 'tagged' },
  nestOptions: { cors: true },          // forwarded to NestFactory.create
});

projectName falls back to process.env.LENSMCP_PROJECT (then 'app') if omitted. The event sink is auto-selected from the environment (see How it fits).

Zero-touch alternative

If the process is already running @lensmcp/node-instrumentation, you do not need this import at all. It grafts LensmcpModule.forRoot({ trace: { autoInstrumentMethods: true }, memory: { mode: 'light', scope: 'all' } }) around your root module the moment NestFactory.create is called. An ordinary main.ts

const app = await NestFactory.create(AppModule);
await app.listen(3100);

— comes up fully instrumented with zero lens code in your source. Roots already wrapped by createLensmcpNestApp carry a marker and are left alone, so the two paths never double-wrap.

Manual module wiring (advanced)

If you build your own root module instead of using the drop-in, import LensmcpModule.forRoot(...) directly:

import { Module } from '@nestjs/common';
import { LensmcpModule } from '@lensmcp/nest-instrumentation';

@Module({
  imports: [LensmcpModule.forRoot({ projectName: 'api' })],
})
export class AppModule {}

Note that forRoot does not default autoInstrumentMethods on — only createLensmcpNestApp and the zero-touch graft do. Imported bare, you opt methods in per-class with @TraceMethod() (or enable trace.autoInstrumentMethods yourself).

What it captures

  • Request flows — an APP_INTERCEPTOR (TraceInterceptor) brackets every HTTP request. It reads x-request-id, x-lensmcp-flow-id, x-lensmcp-origin-node-id, and W3C traceparent headers, opens a LensmcpRequestContext in AsyncLocalStorage, and on completion emits a server-request event (method, route, status, duration) plus a span. Errors are resolved to their real HTTP status (via HttpException.getStatus() or a status / statusCode field) rather than Nest's not-yet-written default.
  • Controller hops — the interceptor times the controller method itself and emits a Controller.handler (12ms) span with role: 'controller', so the trace shows the controller between the request and its services instead of skipping straight to the service.
  • Provider-method traces — when trace.autoInstrumentMethods is on, LensmcpProviderTracker walks the Nest container at bootstrap and wraps each provider's own prototype methods with a traced wrapper (in place, preserving this). Singleton instances are wrapped per-instance; request-scoped / transient providers are wrapped once on the shared prototype so every per-request instance is covered. Lifecycle hooks (onModuleInit, onApplicationBootstrap, etc.), getters/setters, methods already decorated with @TraceMethod, and anything marked @LensmcpIgnore are skipped.
  • N+1 / fan-out detection — each traced method snapshots the request's DB / Redis / external counters on entry; if a method issues ≥3 operations of the same class, it emits a loop warning (e.g. loop in OrdersService.list (12 DB calls)). DB call counts come from @TraceMethod({ db: true }) and from the zero-touch taps in @lensmcp/node-instrumentation (pg, ioredis, fetch/http).
  • Provider graph & lifecycle — every controller and provider emits a singleton-instance event at bootstrap (with module, scope, and a generation counter that increments on each forRoot / hot reload) and a disposed event on module destroy, so the agent can materialise the live provider tree and tell a new generation from a stale one.
  • Memory — when memory.mode !== 'off', container fields (Map / Set / Array) on providers are tracked for size and growth via @lensmcp/memory-tracker. scope: 'all' watches every provider; scope: 'tagged' watches only those marked @TraceProvider({ memory: true }). Each request is bracketed as a "flow" so the suspect-leak detector can compare container sizes at request start vs. after it settles. mode: 'deep' additionally enables heap snapshots.

API

createLensmcpNestApp(appModule, options?)

function createLensmcpNestApp(
  appModule: Type<unknown> | DynamicModule,
  options?: CreateLensmcpNestAppOptions,
): Promise<INestApplication>;

Drop-in for NestFactory.create. Returns a standard INestApplication. Applies the zero-config defaults described above.

CreateLensmcpNestAppOptions extends Partial<LensmcpModuleOptions> (below) and adds:

| Option | Type | Description | | ------------- | ------------------------ | -------------------------------------------------------- | | nestOptions | NestApplicationOptions | Forwarded to NestFactory.create (logger, CORS, etc.). |

LensmcpModule.forRoot(options)

LensmcpModule.forRoot(options: LensmcpModuleOptions): DynamicModule;

Returns a global dynamic module that registers the TraceInterceptor as an APP_INTERCEPTOR and the LensmcpProviderTracker, and (when memory is enabled) configures @lensmcp/memory-tracker. Options resolve synchronously at call time, so @TraceMethod decorators applied to constructor-injected providers find them at decoration time.

LensmcpModuleOptions

| Option | Type | Default | Description | | ------------- | ----------------------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------- | | projectName | string | (required) | Stamped onto every event for resource routing. | | sessionId | string | process.env.LENSMCP_SESSION_ID or a fresh id | Session id stamped onto every event. | | emit | EventSink | env-selected (see below) | Override where events go. | | trace | object (below) | tracing on, db/redis/queues/auto off | Which trace layers to enable. | | memory | { mode?: 'off' \| 'light' \| 'deep'; scope?: 'tagged' \| 'all' } | { mode: 'off', scope: 'tagged' } | Memory container tracking. (createLensmcpNestApp / zero-touch default this to light / all.) |

trace fields (all booleans): requests, guards, controllers, services default true; db, redis, queues default false; autoInstrumentMethods defaults false for bare forRoot and true for createLensmcpNestApp / the zero-touch graft.

Decorators

  • @TraceMethod(name?) / @TraceMethod({ name?, db? }) — trace a single method; emits a span per call. db: true marks it a DB query (emits a db-query event and bumps the request DB counter).
  • @TraceProvider({ memory? }) — opt a provider into memory container tracking when memory.scope === 'tagged'.
  • @LensmcpIgnore() — exclude a class (all methods) or a single method from auto-instrumentation.

Low-level building blocks

Re-exported for advanced use and custom integrations:

  • TraceInterceptor, LensmcpProviderTracker — the interceptor and tracker registered by forRoot.
  • traceWrap(fn, spanName, { db? }), isTraced(fn), isIgnored(target) — the shared method-tracing core.
  • readTraceProvider(ctor) — read @TraceProvider metadata off a class.
  • LENSMCP_CONTEXT_STORAGE, currentLensmcpContext(), runInLensmcpContext(ctx, fn) — the per-request AsyncLocalStorage.
  • defaultEventSink(), inMemorySink() — the env-driven sink and an in-memory sink for tests.

Key types

CreateLensmcpNestAppOptions, LensmcpModuleOptions, ResolvedOptions, EventSink, LensmcpRequestContext, TraceMethodOptions, TraceProviderOptions.

How it fits

@lensmcp/nest-instrumentation is the NestJS adapter in the LensMCP stack. It is grafted automatically by @lensmcp/node-instrumentation under the zero-touch runtime — which patches NestFactory.create and dynamically imports this package to call LensmcpModule.forRoot — so host source stays lens-free. Used by hand, the createLensmcpNestApp drop-in does the same wiring with one import.

Every event it emits is a BaseEvent from @lensmcp/protocol-types (kinds: server-request, span, db-query, loop, singleton-instance), shared by every LensMCP source so they reduce into the same resource views. Memory tracking is delegated to @lensmcp/memory-tracker, and ids / fingerprints come from @lensmcp/core.

By default events are routed by defaultEventSink(), which picks a transport from the environment: LENSMCP_EVENT_FILE (append JSONL), LENSMCP_UDS (reconnecting NDJSON over a Unix domain socket), or LENSMCP_IPC_SOCKET (UDP datagrams); with no session configured it falls back to logging only errors to stdout under a [lensmcp] prefix.


Part of LensMCP. Apache-2.0.