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

lazy-mcp

v2.10.2

Published

A proxy tool that converts normal MCP servers to use lazy-loading pattern with three/four meta-tools

Downloads

1,658

Readme

Lazy MCP Proxy

Pipeline Status

A client-agnostic proxy that converts normal MCP servers to use a lazy-loading pattern, dramatically reducing initial context usage by 90%+ and enabling support for hundreds of commands. It works with any MCP client — Claude Desktop, OpenCode, Cursor, VS Code, and more.

Table of Contents

Features

  • Client-Agnostic: Works with any MCP client — Claude Desktop, OpenCode, Cursor, VS Code, and any other MCP-compatible tool. Unlike client-specific solutions, lazy-mcp works everywhere you do.
  • Multi-Server Aggregation: Aggregate multiple MCP servers with on-demand discovery
  • Lazy Loading: Only discover tools when needed, not upfront
  • Batch Discovery: Discover multiple servers in one call
  • 90%+ Context Reduction: From ~16K to ~1.5K tokens initially
  • Built-in OAuth 2.0 + PKCE: Authenticate with OAuth-protected remote servers without a browser — works in sandboxed agent environments
  • Background Health Monitoring: Probes all servers on startup and periodically; list_servers shows accurate health from the first call
  • Hot Config Reload: Send SIGHUP to reload config without restarting — add, remove, or update servers on the fly
  • Streamable HTTP Transport: Run as an HTTP server — expose lazy-mcp over the network so remote clients can connect via POST /mcp

How It Works

Aggregates multiple MCP servers and exposes four meta-tools:

  • list_servers - Lists all configured MCP servers with health status. Response includes pid and config_file so an agent can fix broken config and reload via kill -HUP <pid>
  • list_commands - Discovers tools from specific server(s), supports batch discovery
  • describe_commands - Gets detailed schemas from a server
  • invoke_command - Executes commands from a specific server

Calling invoke_command

invoke_command is a wrapper meta-tool. Its input should contain only server, command_name, and an optional parameters object. All downstream command inputs must be nested inside parameters.

Correct:

{
  "server": "gitlab-public",
  "command_name": "gitlab_list_pipelines",
  "parameters": {
    "project_id": "gitlab/lazy-mcp",
    "ref": "feat/file-secret-expansion"
  }
}

Incorrect:

{
  "server": "gitlab-public",
  "command_name": "gitlab_list_pipelines",
  "project_id": "gitlab/lazy-mcp",
  "ref": "feat/file-secret-expansion"
}

Known Issue

Some weaker LLM models flatten invoke_command inputs and place downstream tool fields beside parameters instead of nesting them inside parameters. This causes invalid requests, repeated retries, and unnecessary token usage.

If your client supports custom instructions, add a hint like:

When calling lazy-mcp's invoke_command tool:
- put only server and command_name at the top level
- put all downstream tool inputs inside parameters
- never place downstream tool fields beside parameters

Installation

Homebrew (macOS and Linux, no runtime dependencies):

brew tap gitlab-org/lazy-mcp https://gitlab.com/gitlab-org/ai/lazy-mcp
brew install lazy-mcp

Cargo (if you have Rust installed, no Node.js required):

cargo install lazy-mcp

If you have Python / uv (no Node.js required):

uvx lazy-mcp

If you have Node.js — use npx to always get the latest version:

npx lazy-mcp@latest

Or install globally (locks to specific version):

npm install -g lazy-mcp

Docker / Podman:

docker build -t lazy-mcp .
docker compose up

The image compiles the TypeScript CLI during docker build, so this works from a clean checkout without a prebuilt dist/ directory.

Or with Podman:

podman build -t lazy-mcp .
podman compose up

This runs lazy-mcp in HTTP mode on port 8080 with config mounted from ~/.config/lazy-mcp/servers.json. See docker-compose.yml for configuration options.

Usage

Create a configuration file at ~/.config/lazy-mcp/servers.json:

{
  "servers": [
    {
      "name": "chrome-devtools",
      "description": "Chrome DevTools automation",
      "command": ["npx", "-y", "chrome-devtools-mcp@latest"]
    },
    {
      "name": "gitlab",
      "description": "GitLab MCP server",
      "url": "https://gitlab.com/api/v4/mcp"
    },
    {
      "name": "my-remote-server",
      "description": "Custom remote MCP server with static token",
      "url": "https://api.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${API_TOKEN}"
      }
    },
    {
      "name": "glean",
      "description": "Glean enterprise search (OAuth)",
      "url": "https://your-company.glean.com/mcp/default"
    }
  ]
}

Then run:

# Using npx (recommended - always latest version)
npx lazy-mcp@latest --config ~/.config/lazy-mcp/servers.json

# Or via environment variable
LAZY_MCP_CONFIG=~/.config/lazy-mcp/servers.json npx lazy-mcp@latest

# Or if installed globally
lazy-mcp --config ~/.config/lazy-mcp/servers.json

Shell client commands

A running HTTP daemon can serve both MCP hosts and short-lived shell clients:

# Terminal 1: expose this daemon to the shell client commands
lazy-mcp --transport http --cli

# Terminal 2: discover and invoke downstream tools through that daemon
lazy-mcp list-commands glean
lazy-mcp describe glean chat
lazy-mcp call glean search '{"query":"quarterly roadmap"}'
lazy-mcp call glean search '{"query":"quarterly roadmap"}' --json
printf '%s' '{"query":"long prompt"}' | lazy-mcp call glean search -

# Start daemon-owned OAuth and optionally wait for authorization to finish
lazy-mcp auth glean
lazy-mcp auth glean --json
lazy-mcp auth glean --wait

# Print the bundled, version-matched Agent Skill
lazy-mcp skill

call, list-commands, describe, and auth are thin Streamable HTTP MCP clients. They never load server config, spawn downstream servers, or access the OAuth token store. Endpoint discovery checks --url, LAZY_MCP_URL, then the XDG-aware cli-endpoint.json file written only when a daemon starts with --cli. Auto-discovery accepts one live loopback daemon; a second --cli daemon is refused, while ordinary HTTP daemons remain undiscoverable and can be selected with explicit --url. Client requests have a 60-second deadline, configurable with --timeout SECONDS; call parameters can come from stdin with - or from --params-file PATH. auth --json returns the server name, authentication state, and an authUrl when authorization is required. With --wait, it streams the pending and authenticated states as JSON Lines. Bearer authentication checks --auth-token, LAZY_MCP_AUTH_TOKEN, then the mode-0600 endpoint file. Prefer the environment variable because command-line tokens may be visible in process listings. See the HTTP transport guide.

lazy-mcp skill [path] prints the bundled, version-matched Agent Skill for these commands. The skill is embedded in the executable and JavaScript bundle, so it is available from npm global installs, npx, Homebrew, PyPI, and crates.io without a separate asset lookup. Run lazy-mcp skill for SKILL.md; invalid, absolute, or traversal paths exit non-zero and list the valid bundled files.

Config directory location (XDG Base Directory)

The config directory defaults to ~/.config/lazy-mcp/, but lazy-mcp honours the XDG Base Directory Specification: if $XDG_CONFIG_HOME is set, lazy-mcp uses $XDG_CONFIG_HOME/lazy-mcp/ instead. This applies to:

  • the default servers.json location,
  • the OAuth token store (tokens.json, client-info.json, …),
  • the OAuth callback PID lock files.

This makes it easy to scope tokens per project (e.g. via direnv or mise) without having to override $HOME:

# In a project's .envrc / mise.toml:
export XDG_CONFIG_HOME="$PWD/.config"

With that set, lazy-mcp will read ./.config/lazy-mcp/servers.json and store tokens under ./.config/lazy-mcp/ — completely isolated from your global lazy-mcp state.

Streamable HTTP Transport

By default, lazy-mcp communicates over stdio. You can also run it as an HTTP server so remote clients can connect over the network:

lazy-mcp --config servers.json --transport http --port 3000 --auth-token "my-secret"

See doc/HTTP_TRANSPORT.md for full configuration, security guidance (DNS rebinding protection, payload limits, bearer auth), and reverse-proxy setup.

Integration (Claude Desktop, OpenCode, Cursor, VS Code, and more)

Replace multiple MCP server entries in your client with one aggregated lazy-mcp proxy:

{
  "mcp": {
    "lazy-mcp": {
      "type": "local",
      "command": ["npx", "lazy-mcp@latest", "--config", "~/.config/lazy-mcp/servers.json"],
      "enabled": true
    }
  }
}

All downstream MCP servers live in ~/.config/lazy-mcp/servers.json. Result: ~90% context reduction (from ~16K to ~1.5K tokens initially).

See doc/INTEGRATION.md for before/after examples and HTTP-mode client configuration.

Example

# Configure multiple MCP servers in servers.json, then:
npx lazy-mcp@latest --config ~/.config/lazy-mcp/servers.json
# Exposes: list_servers, list_commands, describe_commands, invoke_command (4 meta-tools)
# Instead of loading all tools from all servers upfront (~16K+ tokens),
# the agent discovers tools on-demand (~1.5K tokens initially)

Development

npm install
npm run build
npm test

Running from Local Source

Instead of npx lazy-mcp@latest (which downloads the published package), you can run directly from the cloned repo:

Without building — using ts-node (picks up source changes immediately):

npm run dev -- --config ~/.config/lazy-mcp/servers.json

After building — run the compiled output:

npm run build
node dist/cli.js --config ~/.config/lazy-mcp/servers.json
# or equivalently:
npm start -- --config ~/.config/lazy-mcp/servers.json

In an MCP client config — point directly at the local build:

{
  "mcp": {
    "lazy-mcp": {
      "command": "node",
      "args": ["/path/to/lazy-mcp/dist/cli.js", "--config", "~/.config/lazy-mcp/servers.json"]
    }
  }
}

Or with ts-node (no build needed, always reflects latest source):

{
  "mcp": {
    "lazy-mcp": {
      "command": "npx",
      "args": ["ts-node", "/path/to/lazy-mcp/src/cli.ts", "--config", "~/.config/lazy-mcp/servers.json"]
    }
  }
}

Releases

Releases are fully automated via semantic-release on every push to main. CI analyzes Conventional Commits, bumps the version, tags, and publishes to npm, PyPI, and crates.io.

See doc/RELEASES.md for the full release pipeline and the required CI/CD variables (GITLAB_RELEASE_TOKEN, NPM_TOKEN, PYPI_TOKEN, CARGO_TOKEN).

Configuration Reference

lazy-mcp reads its configuration from ~/.config/lazy-mcp/servers.json (or --config <path>). At minimum each server needs name, description, and either command (local) or url (remote).

Common top-level blocks:

  • servers[] — list of MCP servers to aggregate (required)
  • permissions — global and per-server allow/deny rules for invoke_command (experimental)
  • transport — switch from stdio to HTTP, set port, bind host, bearer auth, etc.
  • logging — structured stderr logging (level, format, body dumps, redaction)
  • healthMonitor — background health probes (activity-driven by default)
  • embedServerSummaries — opt-in: embed configured server names/descriptions in list_servers description
  • forwardPrompts (per-server) — opt-in: forward a server's MCP prompts through lazy-mcp's aggregated prompts/list/prompts/get
  • requestTimeout — per-server request timeout in ms

You can also expand secrets in any string value with ${VAR} (env var) or {file:/path/to/secret} (file-based, owner-only 0600 recommended).

Send SIGHUP to reload the config without restarting (kill -HUP <pid> — the PID is in list_servers).

For the full reference — every field, OAuth flow, permission rule semantics, glob syntax, HTTP transport security, logging knobs, health-monitor tuning, and SIGHUP reload semantics — see doc/CONFIGURATION.md.

Benefits

  • 90%+ context reduction - From ~16K to ~1.5K tokens initially
  • Progressive tool discovery - Only load schemas when needed
  • Multi-server aggregation - Manage multiple MCP servers in one config
  • Batch discovery - Discover multiple servers efficiently
  • Scales to hundreds of commands without context bloat
  • Flexible configuration - Enable/disable servers on demand
  • Environment variable support - Secure credential management via ${VAR} and {file:...} notations
  • Both local and remote - Support for subprocess and HTTP servers
  • Streamable HTTP transport - Run as an HTTP server for remote client access
  • Health monitoring - Background probes detect broken servers before you hit them

Documentation

  • doc/CONFIGURATION.md - Full configuration reference (servers, permissions, OAuth, transport, logging, health monitoring, SIGHUP reload)
  • doc/HTTP_TRANSPORT.md - Streamable HTTP transport setup, security, and reverse-proxy guidance
  • doc/INTEGRATION.md - Client integration examples (Claude Desktop, OpenCode, Cursor, VS Code) for stdio and HTTP modes
  • doc/RELEASES.md - Release pipeline and required CI/CD variables
  • doc/ARCHITECTURE.md - Architecture overview and design patterns
  • doc/CONTRIBUTING.md - Contributing guide with common development tasks
  • doc/requests/ - Bruno API collection for testing the Streamable HTTP transport. Open the doc/requests/ folder as a collection in Bruno, select the local or local-with-auth environment, and run requests against a locally running lazy-mcp --transport http instance.
  • CHANGELOG.md - Version history and release notes
  • AGENTS.md - Development guide for AI coding agents (build commands, code style, testing patterns)