lazy-mcp 2.2.7 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -1
- package/dist/cli.js +274 -35
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +117 -0
- package/dist/config.js.map +1 -1
- package/dist/http-server.d.ts +20 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +567 -0
- package/dist/http-server.js.map +1 -0
- package/dist/logger.d.ts +39 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +237 -0
- package/dist/logger.js.map +1 -0
- package/dist/server-manager.d.ts +4 -1
- package/dist/server-manager.d.ts.map +1 -1
- package/dist/server-manager.js +261 -134
- package/dist/server-manager.js.map +1 -1
- package/dist/server.d.ts +11 -5
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +276 -188
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,9 +12,11 @@ A client-agnostic proxy that converts normal MCP servers to use a lazy-loading p
|
|
|
12
12
|
- [How It Works](#how-it-works)
|
|
13
13
|
- [Installation](#installation)
|
|
14
14
|
- [Usage](#usage)
|
|
15
|
+
- [Streamable HTTP Transport](#streamable-http-transport)
|
|
15
16
|
- [Integration (Claude Desktop, OpenCode, Cursor, VS Code, and more)](#integration-claude-desktop-opencode-cursor-vs-code-and-more)
|
|
16
17
|
- [Example](#example)
|
|
17
18
|
- [Development](#development)
|
|
19
|
+
- [Running from Local Source](#running-from-local-source)
|
|
18
20
|
- [Releases](#releases)
|
|
19
21
|
- [How It Works](#how-it-works)
|
|
20
22
|
- [Required CI/CD Variables](#required-cicd-variables)
|
|
@@ -27,6 +29,8 @@ A client-agnostic proxy that converts normal MCP servers to use a lazy-loading p
|
|
|
27
29
|
- [OAuth 2.0 Authentication](#oauth-20-authentication)
|
|
28
30
|
- [Command Format](#command-format)
|
|
29
31
|
- [Environment Variables](#environment-variables)
|
|
32
|
+
- [Transport Configuration](#transport-configuration)
|
|
33
|
+
- [Logging Configuration](#logging-configuration)
|
|
30
34
|
- [Health Monitoring](#health-monitoring)
|
|
31
35
|
- [Config Reload (SIGHUP)](#config-reload-sighup)
|
|
32
36
|
- [Benefits](#benefits)
|
|
@@ -43,6 +47,7 @@ A client-agnostic proxy that converts normal MCP servers to use a lazy-loading p
|
|
|
43
47
|
- **Built-in OAuth 2.0 + PKCE**: Authenticate with OAuth-protected remote servers without a browser — works in sandboxed agent environments
|
|
44
48
|
- **Background Health Monitoring**: Probes all servers on startup and periodically; `list_servers` shows accurate health from the first call
|
|
45
49
|
- **Hot Config Reload**: Send `SIGHUP` to reload config without restarting — add, remove, or update servers on the fly
|
|
50
|
+
- **Streamable HTTP Transport**: Run as an HTTP server — expose lazy-mcp over the network so remote clients can connect via `POST /mcp`
|
|
46
51
|
|
|
47
52
|
## How It Works
|
|
48
53
|
|
|
@@ -86,6 +91,24 @@ Or install globally (locks to specific version):
|
|
|
86
91
|
npm install -g lazy-mcp
|
|
87
92
|
```
|
|
88
93
|
|
|
94
|
+
**Docker / Podman**:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
docker build -t lazy-mcp .
|
|
98
|
+
docker compose up
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The image compiles the TypeScript CLI during `docker build`, so this works from a clean checkout without a prebuilt `dist/` directory.
|
|
102
|
+
|
|
103
|
+
Or with Podman:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
podman build -t lazy-mcp .
|
|
107
|
+
podman compose up
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
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.
|
|
111
|
+
|
|
89
112
|
## Usage
|
|
90
113
|
|
|
91
114
|
Create a configuration file at `~/.config/lazy-mcp/servers.json`:
|
|
@@ -133,6 +156,60 @@ LAZY_MCP_CONFIG=~/.config/lazy-mcp/servers.json npx lazy-mcp@latest
|
|
|
133
156
|
lazy-mcp --config ~/.config/lazy-mcp/servers.json
|
|
134
157
|
```
|
|
135
158
|
|
|
159
|
+
### Streamable HTTP Transport
|
|
160
|
+
|
|
161
|
+
By default, lazy-mcp communicates over **stdio** (standard MCP transport). You can also run it as an **HTTP server** using the Streamable HTTP transport, allowing remote clients to connect over the network:
|
|
162
|
+
|
|
163
|
+
**Via config file** — add a `transport` block to `servers.json`:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"servers": [
|
|
168
|
+
{ "name": "my-server", "description": "My MCP server", "command": ["python", "server.py"] }
|
|
169
|
+
],
|
|
170
|
+
"transport": {
|
|
171
|
+
"type": "http",
|
|
172
|
+
"port": 3000,
|
|
173
|
+
"host": "localhost",
|
|
174
|
+
"path": "/mcp",
|
|
175
|
+
"authToken": "${MY_API_KEY}"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Via CLI flags** (override config values):
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Start HTTP server on port 3000
|
|
184
|
+
lazy-mcp --config servers.json --transport http --port 3000
|
|
185
|
+
|
|
186
|
+
# With custom host, path, and auth
|
|
187
|
+
lazy-mcp --config servers.json --transport http --port 3000 --host 127.0.0.1 --path /mcp --auth-token "my-secret"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Security note:** `--host 0.0.0.0` exposes the HTTP server on all network interfaces. Use it only inside Docker or trusted networks.
|
|
191
|
+
|
|
192
|
+
**Via environment variables** (lowest precedence):
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
LAZY_MCP_TRANSPORT=http LAZY_MCP_PORT=3000 LAZY_MCP_AUTH_TOKEN=my-secret lazy-mcp
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Precedence**: CLI flags > config file > environment variables > defaults.
|
|
199
|
+
|
|
200
|
+
Once running, clients connect as a remote MCP server:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Quick test with curl
|
|
204
|
+
curl -X POST http://localhost:3000/mcp \
|
|
205
|
+
-H "Content-Type: application/json" \
|
|
206
|
+
-H "Accept: application/json, text/event-stream" \
|
|
207
|
+
-H "Authorization: Bearer ${MY_API_KEY}" \
|
|
208
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
> **Note**: Server-initiated notifications via GET/SSE are not supported in stateless mode. Use POST for all requests.
|
|
212
|
+
|
|
136
213
|
## Integration (Claude Desktop, OpenCode, Cursor, VS Code, and more)
|
|
137
214
|
|
|
138
215
|
Replace multiple MCP server entries with one aggregated proxy:
|
|
@@ -163,6 +240,24 @@ Replace multiple MCP server entries with one aggregated proxy:
|
|
|
163
240
|
}
|
|
164
241
|
```
|
|
165
242
|
|
|
243
|
+
**HTTP mode** — for clients that support remote MCP servers:
|
|
244
|
+
|
|
245
|
+
Start lazy-mcp in HTTP mode (e.g. `lazy-mcp --config servers.json --transport http --port 3000`), then configure your client:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"mcp": {
|
|
250
|
+
"lazy-mcp": {
|
|
251
|
+
"type": "remote",
|
|
252
|
+
"url": "http://localhost:3000/mcp",
|
|
253
|
+
"headers": {
|
|
254
|
+
"Authorization": "Bearer ${LAZY_MCP_AUTH_TOKEN}"
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
166
261
|
Where `~/.config/lazy-mcp/servers.json` contains all 5 servers:
|
|
167
262
|
```json
|
|
168
263
|
{
|
|
@@ -196,6 +291,51 @@ npm run build
|
|
|
196
291
|
npm test
|
|
197
292
|
```
|
|
198
293
|
|
|
294
|
+
### Running from Local Source
|
|
295
|
+
|
|
296
|
+
Instead of `npx lazy-mcp@latest` (which downloads the published package), you can run directly from the cloned repo:
|
|
297
|
+
|
|
298
|
+
**Without building** — using `ts-node` (picks up source changes immediately):
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
npm run dev -- --config ~/.config/lazy-mcp/servers.json
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**After building** — run the compiled output:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
npm run build
|
|
308
|
+
node dist/cli.js --config ~/.config/lazy-mcp/servers.json
|
|
309
|
+
# or equivalently:
|
|
310
|
+
npm start -- --config ~/.config/lazy-mcp/servers.json
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**In an MCP client config** — point directly at the local build:
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"mcp": {
|
|
318
|
+
"lazy-mcp": {
|
|
319
|
+
"command": "node",
|
|
320
|
+
"args": ["/path/to/lazy-mcp/dist/cli.js", "--config", "~/.config/lazy-mcp/servers.json"]
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Or with `ts-node` (no build needed, always reflects latest source):
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"mcp": {
|
|
331
|
+
"lazy-mcp": {
|
|
332
|
+
"command": "npx",
|
|
333
|
+
"args": ["ts-node", "/path/to/lazy-mcp/src/cli.ts", "--config", "~/.config/lazy-mcp/servers.json"]
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
199
339
|
## Releases
|
|
200
340
|
|
|
201
341
|
Releases are fully automated via [semantic-release](https://semantic-release.gitbook.io/) on every push to `main`.
|
|
@@ -393,6 +533,80 @@ Use `${VAR_NAME}` to reference environment variables:
|
|
|
393
533
|
}
|
|
394
534
|
```
|
|
395
535
|
|
|
536
|
+
### Transport Configuration
|
|
537
|
+
|
|
538
|
+
Configure how lazy-mcp exposes its MCP endpoint. By default, it uses stdio (for subprocess-based clients). Set `type: "http"` to run as an HTTP server.
|
|
539
|
+
|
|
540
|
+
**Top-level configuration** (in `servers.json`):
|
|
541
|
+
|
|
542
|
+
| Field | Type | Default | Description |
|
|
543
|
+
|-------|------|---------|-------------|
|
|
544
|
+
| `transport.type` | `"stdio"` \| `"http"` | `"stdio"` | Transport mode |
|
|
545
|
+
| `transport.port` | number | `8080` | HTTP server port |
|
|
546
|
+
| `transport.host` | string | `"127.0.0.1"` | HTTP server bind address |
|
|
547
|
+
| `transport.path` | string | `"/mcp"` | MCP endpoint URL path |
|
|
548
|
+
| `transport.authToken` | string | — | Bearer token for HTTP auth (supports `${VAR}` expansion) |
|
|
549
|
+
| `transport.allowedOrigins` | string[] | — | List of allowed Origin header values for request-origin validation / CSRF protection (e.g. `["https://example.com"]`). Full origins including scheme and port. Does not send CORS response headers. |
|
|
550
|
+
| `transport.allowedHosts` | string[] | — | List of explicitly allowed host domains for DNS rebinding protection |
|
|
551
|
+
| `transport.maxPayloadSize` | number | `4194304` | Maximum request body size in bytes. Requests exceeding this limit return HTTP 413 |
|
|
552
|
+
|
|
553
|
+
**CLI flags** (override config values):
|
|
554
|
+
|
|
555
|
+
| Flag | Env Variable | Description |
|
|
556
|
+
|------|-------------|-------------|
|
|
557
|
+
| `--transport` | `LAZY_MCP_TRANSPORT` | Transport type (`stdio` or `http`) |
|
|
558
|
+
| `--port` | `LAZY_MCP_PORT` | HTTP server port |
|
|
559
|
+
| `--host` | `LAZY_MCP_HOST` | HTTP server bind address |
|
|
560
|
+
| `--path` | `LAZY_MCP_PATH` | MCP endpoint URL path |
|
|
561
|
+
| `--auth-token` | `LAZY_MCP_AUTH_TOKEN` | Bearer token for HTTP auth |
|
|
562
|
+
| `--request-timeout` | `LAZY_MCP_REQUEST_TIMEOUT` | Request timeout in ms for server calls, including MCP handshake requests and remote response parsing (default: 10000) |
|
|
563
|
+
| `--max-payload-size` | `LAZY_MCP_MAX_PAYLOAD_SIZE` | Maximum request body size in bytes (default: 4194304) |
|
|
564
|
+
|
|
565
|
+
When `authToken` is set, all HTTP requests must include `Authorization: Bearer <token>` — unauthenticated requests receive `401 Unauthorized`.
|
|
566
|
+
|
|
567
|
+
### Logging Configuration
|
|
568
|
+
|
|
569
|
+
lazy-mcp now emits structured logs to **stderr** (stdout remains reserved for MCP protocol traffic).
|
|
570
|
+
|
|
571
|
+
**Top-level configuration** (in `servers.json`):
|
|
572
|
+
|
|
573
|
+
| Field | Type | Default | Description |
|
|
574
|
+
|-------|------|---------|-------------|
|
|
575
|
+
| `logging.level` | `"error"` \| `"info"` \| `"debug"` | `"info"` | Minimum log level |
|
|
576
|
+
| `logging.format` | `"json"` \| `"plain"` | `"json"` | Log output format |
|
|
577
|
+
| `logging.dumpBodies` | boolean | `false` | Enable debug request/response body dumps |
|
|
578
|
+
| `logging.maxBodyLogBytes` | number | `8192` | Max body-dump size in bytes before truncation |
|
|
579
|
+
| `logging.redactKeys` | string[] | — | Additional case-insensitive keys to redact (merged with built-in defaults) |
|
|
580
|
+
|
|
581
|
+
Built-in redaction includes common secret keys like `authorization`, `token`, `access_token`, `refresh_token`, `client_secret`, and `headers.authorization`.
|
|
582
|
+
|
|
583
|
+
Example:
|
|
584
|
+
|
|
585
|
+
```json
|
|
586
|
+
{
|
|
587
|
+
"servers": [
|
|
588
|
+
{
|
|
589
|
+
"name": "my-server",
|
|
590
|
+
"description": "Example",
|
|
591
|
+
"command": ["npx", "-y", "my-mcp-server"]
|
|
592
|
+
}
|
|
593
|
+
],
|
|
594
|
+
"logging": {
|
|
595
|
+
"level": "debug",
|
|
596
|
+
"format": "json",
|
|
597
|
+
"dumpBodies": true,
|
|
598
|
+
"maxBodyLogBytes": 4096,
|
|
599
|
+
"redactKeys": ["my_custom_secret"]
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
Typical access log event fields include:
|
|
605
|
+
- client source (`clientIp`, optional `forwardedFor`)
|
|
606
|
+
- request shape (`httpMethod`, `path`, `mcpMethod`, `lazyTool`)
|
|
607
|
+
- downstream routing (`downstreamServer`, `downstreamCommand`)
|
|
608
|
+
- outcome (`status`, `reason`, `durationMs`)
|
|
609
|
+
|
|
396
610
|
### Health Monitoring
|
|
397
611
|
|
|
398
612
|
lazy-mcp includes a background health monitor that probes all servers periodically. The monitor is **activity-driven**: it sleeps on startup and only begins probing after the first user tool call (`list_servers`, `list_commands`, etc.). After a configurable idle timeout (default: 5 minutes) with no tool calls, the monitor goes back to sleep. This prevents OAuth-protected servers (e.g. GitLab via `mcp-remote`) from opening browser windows when no one is using the tools.
|
|
@@ -407,8 +621,9 @@ Successful probes populate the discovery cache, so subsequent `list_commands` ca
|
|
|
407
621
|
| `healthMonitor.interval` | number | `30000` | Interval between health checks (ms) |
|
|
408
622
|
| `healthMonitor.timeout` | number | `10000` | Timeout per server probe (ms) |
|
|
409
623
|
| `healthMonitor.idleTimeout` | number | `300000` | Stop probing after this much inactivity (ms). `0` = never sleep (legacy) |
|
|
624
|
+
| `requestTimeout` | number | `10000` | Timeout for individual server requests. For remote HTTP servers, this includes waiting for headers, reading response bodies, and SSE response parsing. Override via `--request-timeout` or `LAZY_MCP_REQUEST_TIMEOUT` |
|
|
410
625
|
|
|
411
|
-
To disable:
|
|
626
|
+
To disable health monitoring:
|
|
412
627
|
```json
|
|
413
628
|
{
|
|
414
629
|
"servers": [...],
|
|
@@ -416,6 +631,14 @@ To disable:
|
|
|
416
631
|
}
|
|
417
632
|
```
|
|
418
633
|
|
|
634
|
+
To increase the request timeout (e.g. for slow remote servers):
|
|
635
|
+
```json
|
|
636
|
+
{
|
|
637
|
+
"servers": [...],
|
|
638
|
+
"requestTimeout": 30000
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
419
642
|
### Config Reload (SIGHUP)
|
|
420
643
|
|
|
421
644
|
You can reload the configuration without restarting the process by sending a `SIGHUP` signal:
|
|
@@ -446,6 +669,7 @@ If the new config is invalid, the reload is rejected and the current config cont
|
|
|
446
669
|
- **Flexible configuration** - Enable/disable servers on demand
|
|
447
670
|
- **Environment variable support** - Secure credential management
|
|
448
671
|
- **Both local and remote** - Support for subprocess and HTTP servers
|
|
672
|
+
- **Streamable HTTP transport** - Run as an HTTP server for remote client access
|
|
449
673
|
- **Health monitoring** - Background probes detect broken servers before you hit them
|
|
450
674
|
|
|
451
675
|
## Documentation
|
|
@@ -454,4 +678,5 @@ If the new config is invalid, the reload is rejected and the current config cont
|
|
|
454
678
|
- **[AGENTS.md](./AGENTS.md)** - Development guide for AI coding agents (build commands, code style, testing patterns)
|
|
455
679
|
- **[doc/ARCHITECTURE.md](./doc/ARCHITECTURE.md)** - Architecture overview and design patterns
|
|
456
680
|
- **[doc/CONTRIBUTING.md](./doc/CONTRIBUTING.md)** - Contributing guide with common development tasks
|
|
681
|
+
- **[doc/requests/](./doc/requests/)** - [Bruno](https://www.usebruno.com/) 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.
|
|
457
682
|
- **[Configuration Reference](#configuration-reference)** - Server configuration options (above)
|