@sarthakpranesh/mcp-lazy-proxy 0.1.3 → 0.1.5

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 CHANGED
@@ -34,21 +34,23 @@ Other lazy MCP proxies are all-or-nothing: they either keep every backend lazy o
34
34
 
35
35
  ## Quick start
36
36
 
37
- **Requirements:** Node.js 18+. Install and build:
37
+ The proxy runs in one of two modes:
38
38
 
39
- ```bash
40
- npm install
41
- npm run build
42
- ```
39
+ | Mode | How it runs | Traffic flow |
40
+ | ---- | ----------- | ------------ |
41
+ | **Stdio (local)** | Spawned as a subprocess by your MCP client (default). | Proxy talks to backends directly from your machine. **No network server is started and no remote proxy call is made** — it all happens in-process over stdio, local to your client. Best for a single machine. |
42
+ | **Self-hosted HTTP (shared)** | Runs as a long-lived HTTP server (e.g. in Docker) on one host. | The proxy is a **central gate for all MCP calls** for every machine/agent — each client talks to the proxy over HTTP with a bearer token, and the proxy fans out to backends on its own host. Best for a team sharing one config. |
43
+
44
+ In both modes the model sees the same two meta-tools (`get_mcp_tools`, `call_mcp_tool`); only the transport differs.
43
45
 
44
- Create a config file pointing at your MCP servers. See [Configuration](#configuration) for the full shape.
46
+ Start with a config file pointing at your MCP servers. See [Configuration](#configuration) for the full shape.
45
47
 
46
48
  ```json
47
49
  {
48
50
  "mcpServers": {
49
51
  "github": {
50
52
  "url": "https://api.githubcopilot.com/mcp/",
51
- "headers": { "Authorization": "Bearer <TOKEN>" },
53
+ "headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" },
52
54
  "instruction": "GitHub: issues, pull requests, code search. Use for anything repo-related."
53
55
  },
54
56
  "local-tool": {
@@ -61,7 +63,9 @@ Create a config file pointing at your MCP servers. See [Configuration](#configur
61
63
  }
62
64
  ```
63
65
 
64
- Point your MCP client at it as a stdio server. For example, in an MCP client config:
66
+ ### Option A Stdio (local, default)
67
+
68
+ Point your MCP client at it as a stdio server. No network server is started and no remote call is made — the proxy runs as a subprocess on the same machine as your client. For example, in an MCP client config:
65
69
 
66
70
  ```json
67
71
  {
@@ -74,9 +78,66 @@ Point your MCP client at it as a stdio server. For example, in an MCP client con
74
78
  }
75
79
  ```
76
80
 
81
+ ### Option B — Self-hosted HTTP (shared)
82
+
83
+ Run the proxy as a shared, long-lived HTTP server (e.g. in Docker) that many clients connect to over the network. No web UI — clients reach the same two meta-tools (`get_mcp_tools`, `call_mcp_tool`) over MCP Streamable HTTP at `POST /mcp`. The proxy becomes the **single central gate**: every MCP call from every user machine/agent flows through it, and it fans out to backends on its own host.
84
+
85
+ 1. Create `.env`** with the shared bearer token and any backend secrets. Start from the example:
86
+ ```dotenv
87
+ # shared token every client must send, for auth
88
+ MCP_AUTH_TOKEN=change-me-to-a-long-random-string
89
+
90
+ # MCP server tokens used by backends in the proxy
91
+ # referenced by mcp.json
92
+ GITHUB_TOKEN=ghp_xxxx
93
+ ```
94
+
95
+ > Generate a strong token, e.g. `openssl rand -hex 32`.
96
+
97
+ 2. Define `docker-compose.yml` to build and run the proxy container, exposing `3000`, mounting `mcp.json` read-only, and passing the env vars through:
98
+
99
+ ```yaml
100
+ services:
101
+ mcp-lazy-proxy:
102
+ image: sarthakpranesh/mcp-lazy-proxy
103
+ ports:
104
+ - "3000:3000"
105
+ environment:
106
+ - MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN}
107
+ - GITHUB_TOKEN=${GITHUB_TOKEN}
108
+ volumes:
109
+ - ./mcp.json:/app/mcp.json:ro
110
+ restart: unless-stopped
111
+ ```
112
+
113
+ 3. Start it
114
+ ```bash
115
+ docker compose up -d
116
+ ```
117
+
118
+ 4. Each machine connects with a single entry in its MCP client config:
119
+ ```json
120
+ {
121
+ "mcpServers": {
122
+ "lazy-proxy": {
123
+ "type": "http",
124
+ "url": "http://<host>:3000/mcp",
125
+ "headers": { "Authorization": "Bearer <MCP_AUTH_TOKEN>" }
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ Notes:
132
+ - **Local stdio backends now run on the proxy host, not the client.** Backends defined with `command` (plus any local files they need) must exist and be reachable inside the container. Remote (`url`) backends behave the same as in stdio mode.
133
+ - **Auth is mandatory.** In HTTP mode the proxy refuses to start unless `MCP_AUTH_TOKEN` is set. Every request to `/mcp` must carry `Authorization: Bearer <token>` or it is rejected with `401`.
134
+ - **Secrets stay out of the mounted config.** `mcp.json` can reference environment variables with `${VAR}` (e.g. `"Authorization": "Bearer ${GITHUB_TOKEN}"`) and they are substituted from `process.env` at load time.
135
+ - **Config** still comes from `--config` (default `/app/mcp.json`, matching the container mount). The CLI flags are: `--transport http` (stdio is the default), `--port <n>` (default `3000`), `--host <addr>` (default `0.0.0.0`).
136
+ **Rotating the bearer token:** generate a new value, set it in `.env` as `MCP_AUTH_TOKEN`, then `docker compose up -d` to recreate the container. Existing clients must be updated with the new token — old tokens are rejected immediately because each request is checked against the current value.
137
+
77
138
  ## Configuration
78
139
 
79
- The proxy takes a single JSON config file via `--config <path>`. It must have an `mcpServers` object; each entry is a backend with either a `url` (remote) or a `command` (local).
140
+ The proxy takes a single JSON config file via `--config <path>`. It must have an `mcpServers` object; each entry is a backend with either a `url` (remote) or a `command` (local). Any `${VAR}` reference inside the file is substituted from `process.env` at load time, so secrets can live in environment variables instead of the mounted file.
80
141
 
81
142
  ### Backend reference
82
143
 
package/dist/config.js CHANGED
@@ -19,9 +19,21 @@ export function loadConfig(path) {
19
19
  }
20
20
  return { mcpServers: servers };
21
21
  }
22
- // read and parse a JSON file into a plain object.
22
+ // substitute ${VAR} references in the config text with values from process.env
23
+ // so secrets can stay out of the mounted file (e.g. ${GITHUB_TOKEN}).
24
+ function substituteEnv(text) {
25
+ return text.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (match, name) => {
26
+ const value = process.env[name];
27
+ if (value === undefined) {
28
+ throw new Error(`environment variable "${name}" referenced in config is not set`);
29
+ }
30
+ // raw substitution so it works inside JSON strings, e.g. "Bearer ${TOKEN}"
31
+ return value;
32
+ });
33
+ }
34
+ // read, env-substitute, and parse a JSON file into a plain object.
23
35
  function readJson(path) {
24
36
  const text = readFileSync(path, "utf8");
25
- return JSON.parse(text);
37
+ return JSON.parse(substituteEnv(text));
26
38
  }
27
39
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA8BvC,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC;IAC/B,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,oCAAoC,CAAC,CAAC;IACvE,CAAC;IACD,iDAAiD;IACjD,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,qBAAqB,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,MAAM,GAAG,OAAQ,GAA2B,CAAC,GAAG,KAAK,QAAQ,CAAC;QACpE,MAAM,UAAU,GAAG,OAAQ,GAA0B,CAAC,OAAO,KAAK,QAAQ,CAAC;QAC3E,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,kCAAkC,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,OAAwC,EAAE,CAAC;AAClE,CAAC;AAED,kDAAkD;AAClD,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA8BvC,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC;IAC/B,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,oCAAoC,CAAC,CAAC;IACvE,CAAC;IACD,iDAAiD;IACjD,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,qBAAqB,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,MAAM,GAAG,OAAQ,GAA2B,CAAC,GAAG,KAAK,QAAQ,CAAC;QACpE,MAAM,UAAU,GAAG,OAAQ,GAA0B,CAAC,OAAO,KAAK,QAAQ,CAAC;QAC3E,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,kCAAkC,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,OAAwC,EAAE,CAAC;AAClE,CAAC;AAED,+EAA+E;AAC/E,sEAAsE;AACtE,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,iCAAiC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,mCAAmC,CAAC,CAAC;QACpF,CAAC;QACD,2EAA2E;QAC3E,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC;AAED,mEAAmE;AACnE,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;AACzC,CAAC"}
package/dist/index.js CHANGED
@@ -5,8 +5,13 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
5
5
  import { homedir } from "node:os";
6
6
  import { loadConfig } from "./config.js";
7
7
  import { BackendManager } from "./backend.js";
8
+ import { startHttpServer } from "./server.js";
8
9
  import packageJson from "../package.json" with { type: "json" };
9
10
  const CONFIG_ARG = "--config";
11
+ const TRANSPORT_ARG = "--transport";
12
+ const PORT_ARG = "--port";
13
+ const HOST_ARG = "--host";
14
+ const DEFAULT_CONFIG_PATH = "/app/mcp.json";
10
15
  // expand a leading "~" to the user's home directory, since the proxy may be
11
16
  // spawned without a shell (e.g. by an MCP client) where "~" is not expanded.
12
17
  function expandHome(p) {
@@ -16,141 +21,204 @@ function expandHome(p) {
16
21
  return `${homedir()}${p.slice(1)}`;
17
22
  return p;
18
23
  }
19
- // parse the --config <path> CLI argument.
24
+ // read a required CLI value following the given flag; throws if missing.
25
+ function readValue(argv, flag) {
26
+ const idx = argv.indexOf(flag);
27
+ if (idx === -1)
28
+ return undefined;
29
+ const value = argv[idx + 1];
30
+ if (value === undefined || value.startsWith("--"))
31
+ return undefined;
32
+ return value;
33
+ }
34
+ // parse CLI arguments. stdio is the default transport; http is opt-in.
20
35
  function parseArgs(argv) {
21
- const idx = argv.indexOf(CONFIG_ARG);
22
- if (idx === -1 || !argv[idx + 1]) {
23
- throw new Error(`usage: mcp-lazy-proxy ${CONFIG_ARG} <path-to-config.json>`);
36
+ const configPath = expandHome(readValue(argv, CONFIG_ARG) ?? process.env.MCP_CONFIG_PATH ?? DEFAULT_CONFIG_PATH);
37
+ const transport = (readValue(argv, TRANSPORT_ARG) ?? "stdio");
38
+ if (transport !== "stdio" && transport !== "http") {
39
+ throw new Error(`unsupported --transport "${transport}" (expected "stdio" or "http")`);
24
40
  }
25
- return { configPath: expandHome(argv[idx + 1]) };
41
+ const port = readValue(argv, PORT_ARG) !== undefined ? Number(readValue(argv, PORT_ARG)) : 3000;
42
+ const host = readValue(argv, HOST_ARG) ?? "0.0.0.0";
43
+ return { configPath, transport, port, host };
26
44
  }
27
- // load config and build the backend manager.
28
- const { configPath } = parseArgs(process.argv.slice(2));
29
- const config = loadConfig(configPath);
30
- const backends = new BackendManager(config.mcpServers);
31
- // eagerly load the tool schemas of any favorite backends so they are injected
32
- // into context up front (remaining backends stay lazy). also returns a map of
33
- // each eager tool name to its owning backend so direct calls route correctly.
34
- const { tools: favoriteTools, owner: favoriteOwner } = await backends.loadFavorites();
35
- // advertised server instructions: a brief blurb plus the backend catalog.
36
- const instructions = [
37
- "Lazy MCP proxy. Backend MCP servers are not loaded into context by default; discover them on demand.",
38
- "Available backends:",
39
- ...backends
40
- .list()
41
- .map((b) => `- ${b.name}: ${b.instruction ?? "no description"}`),
42
- "Workflow: call get_mcp_tools to load a backend's tool schemas, then call_mcp_tool to invoke one.",
43
- ].join("\n");
44
- // create the MCP server with the tools capability flag.
45
- // capabilities.tools is a capability flag (not the tool list); the two
46
- // meta-tools are served by the ListToolsRequestSchema handler below.
47
- const server = new Server({ name: packageJson.name, version: packageJson.version }, { capabilities: { tools: {} }, instructions });
48
- // advertise the meta-tools plus any eagerly-loaded favorite backend schemas.
49
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
50
- tools: [
51
- ...favoriteTools,
52
- {
53
- name: "get_mcp_tools",
54
- description: "Discover the tools exposed by a configured MCP backend. Returns the backend's instruction (if any) plus a filtered list of tool names, descriptions, and input schemas. Call this before call_mcp_tool to learn what a backend can do.",
55
- inputSchema: {
56
- type: "object",
57
- properties: {
58
- mcp: { type: "string", description: "Name of the MCP backend from the proxy config." },
59
- query: { type: "string", description: "Optional substring filter on tool name/description." },
60
- limit: { type: "number", description: "Optional cap on results (default 50)." },
61
- },
62
- required: ["mcp"],
63
- },
64
- },
65
- {
66
- name: "call_mcp_tool",
67
- description: "Invoke a tool on a configured MCP backend. Use the tool name and arguments returned by get_mcp_tools.",
68
- inputSchema: {
69
- type: "object",
70
- properties: {
71
- mcp: { type: "string", description: "Name of the MCP backend from the proxy config." },
72
- tool: { type: "string", description: "Tool name as returned by get_mcp_tools." },
73
- arguments: {
45
+ async function main() {
46
+ // parse CLI, load config, and build the backend manager.
47
+ const { configPath, transport, port, host } = parseArgs(process.argv.slice(2));
48
+ const config = loadConfig(configPath);
49
+ const backends = new BackendManager(config.mcpServers);
50
+ // eagerly load the tool schemas of any favorite backends so they are injected
51
+ // into context up front (remaining backends stay lazy). also returns a map of
52
+ // each eager tool name to its owning backend so direct calls route correctly.
53
+ const { tools: favoriteTools, owner: favoriteOwner } = await backends.loadFavorites();
54
+ // shared shutdown: close all backend connections.
55
+ const shutdown = async () => {
56
+ await backends.closeAll();
57
+ };
58
+ // build a fresh MCP server with the meta-tool handlers wired in.
59
+ // a new instance is created per HTTP session (one transport per Server).
60
+ // note: we intentionally use the low-level `Server` (deprecated in the SDK in
61
+ // favor of `McpServer`) because tool schemas are discovered at runtime and
62
+ // forwarded by name via call_mcp_tool the documented advanced use case.
63
+ const buildServer = () => {
64
+ const server = new Server({ name: packageJson.name, version: packageJson.version }, {
65
+ capabilities: { tools: {} },
66
+ instructions: [
67
+ "Lazy MCP proxy. Backend MCP servers are not loaded into context by default; discover them on demand.",
68
+ "Available backends:",
69
+ ...backends
70
+ .list()
71
+ .map((b) => `- ${b.name}: ${b.instruction ?? "no description"}`),
72
+ "Workflow: call get_mcp_tools to load a backend's tool schemas, then call_mcp_tool to invoke one.",
73
+ ].join("\n"),
74
+ });
75
+ // advertise the meta-tools plus any eagerly-loaded favorite backend schemas.
76
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
77
+ tools: [
78
+ ...favoriteTools,
79
+ {
80
+ name: "get_mcp_tools",
81
+ description: "Discover the tools exposed by a configured MCP backend. Returns the backend's instruction (if any) plus a filtered list of tool names, descriptions, and input schemas. Call this before call_mcp_tool to learn what a backend can do.",
82
+ inputSchema: {
74
83
  type: "object",
75
- description: "Arguments per the tool's input schema.",
76
- additionalProperties: true,
84
+ properties: {
85
+ mcp: { type: "string", description: "Name of the MCP backend from the proxy config." },
86
+ query: { type: "string", description: "Optional substring filter on tool name/description." },
87
+ limit: { type: "number", description: "Optional cap on results (default 50)." },
88
+ },
89
+ required: ["mcp"],
77
90
  },
78
91
  },
79
- required: ["mcp", "tool", "arguments"],
80
- },
81
- },
82
- ],
83
- }));
84
- // handle calls to the two meta-tools, dispatching to the right backend.
85
- server.setRequestHandler(CallToolRequestSchema, async (req) => {
86
- const { name, arguments: args } = req.params;
87
- try {
88
- // get_mcp_tools: load a backend's tool schemas (with optional filter/limit).
89
- if (name === "get_mcp_tools") {
90
- const mcp = String(args?.mcp ?? "");
91
- const query = args?.query ? String(args.query) : undefined;
92
- const limit = args?.limit != null ? Number(args.limit) : 50;
93
- const handle = await backends.get(mcp);
94
- const tools = await handle.listTools(query, limit);
95
- return {
96
- content: [
97
- {
98
- type: "text",
99
- text: JSON.stringify({ mcp, instruction: handle.instruction ?? null, tools }, null, 2),
100
- },
101
- ],
102
- };
103
- }
104
- // call_mcp_tool: forward a tool invocation to a backend.
105
- if (name === "call_mcp_tool") {
106
- const mcp = String(args?.mcp ?? "");
107
- const tool = String(args?.tool ?? "");
108
- const toolArgs = (args?.arguments ?? {});
109
- const handle = await backends.get(mcp);
110
- const result = await handle.callTool(tool, toolArgs);
111
- return {
112
- content: [
113
- {
114
- type: "text",
115
- text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
116
- },
117
- ],
118
- };
119
- }
120
- // direct call to an injected favorite tool: route to its owning backend.
121
- const ownerObj = favoriteOwner.get(name);
122
- if (ownerObj) {
123
- const handle = await backends.get(ownerObj.backend);
124
- const result = await handle.callTool(ownerObj.realToolName, (args ?? {}));
125
- return {
126
- content: [
127
- {
128
- type: "text",
129
- text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
92
+ {
93
+ name: "call_mcp_tool",
94
+ description: "Invoke a tool on a configured MCP backend. Use the tool name and arguments returned by get_mcp_tools.",
95
+ inputSchema: {
96
+ type: "object",
97
+ properties: {
98
+ mcp: { type: "string", description: "Name of the MCP backend from the proxy config." },
99
+ tool: { type: "string", description: "Tool name as returned by get_mcp_tools." },
100
+ arguments: {
101
+ type: "object",
102
+ description: "Arguments per the tool's input schema.",
103
+ additionalProperties: true,
104
+ },
105
+ },
106
+ required: ["mcp", "tool", "arguments"],
130
107
  },
131
- ],
132
- };
108
+ },
109
+ ],
110
+ }));
111
+ // handle calls to the two meta-tools, dispatching to the right backend.
112
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
113
+ const { name, arguments: args } = req.params;
114
+ try {
115
+ // get_mcp_tools: load a backend's tool schemas (with optional filter/limit).
116
+ if (name === "get_mcp_tools") {
117
+ const mcp = String(args?.mcp ?? "");
118
+ const query = args?.query ? String(args.query) : undefined;
119
+ const limit = args?.limit != null ? Number(args.limit) : 50;
120
+ const handle = await backends.get(mcp);
121
+ const tools = await handle.listTools(query, limit);
122
+ return {
123
+ content: [
124
+ {
125
+ type: "text",
126
+ text: JSON.stringify({ mcp, instruction: handle.instruction ?? null, tools }, null, 2),
127
+ },
128
+ ],
129
+ };
130
+ }
131
+ // call_mcp_tool: forward a tool invocation to a backend.
132
+ if (name === "call_mcp_tool") {
133
+ const mcp = String(args?.mcp ?? "");
134
+ const tool = String(args?.tool ?? "");
135
+ const toolArgs = (args?.arguments ?? {});
136
+ const handle = await backends.get(mcp);
137
+ const result = await handle.callTool(tool, toolArgs);
138
+ return {
139
+ content: [
140
+ {
141
+ type: "text",
142
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
143
+ },
144
+ ],
145
+ };
146
+ }
147
+ // direct call to an injected favorite tool: route to its owning backend.
148
+ const ownerObj = favoriteOwner.get(name);
149
+ if (ownerObj) {
150
+ const handle = await backends.get(ownerObj.backend);
151
+ const result = await handle.callTool(ownerObj.realToolName, (args ?? {}));
152
+ return {
153
+ content: [
154
+ {
155
+ type: "text",
156
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
157
+ },
158
+ ],
159
+ };
160
+ }
161
+ return { content: [{ type: "text", text: `unknown tool: ${name}` }], isError: true };
162
+ }
163
+ catch (err) {
164
+ return {
165
+ content: [{ type: "text", text: `error: ${err.message}` }],
166
+ isError: true,
167
+ };
168
+ }
169
+ });
170
+ return server;
171
+ };
172
+ // dispatch on the chosen transport.
173
+ if (transport === "http") {
174
+ // bearer token auth is required in HTTP mode: fail fast if unset.
175
+ const token = process.env.MCP_AUTH_TOKEN;
176
+ if (!token) {
177
+ throw new Error("MCP_AUTH_TOKEN must be set in HTTP mode to protect the endpoint. Refusing to start unauthenticated.");
133
178
  }
134
- return { content: [{ type: "text", text: `unknown tool: ${name}` }], isError: true };
135
- }
136
- catch (err) {
137
- return {
138
- content: [{ type: "text", text: `error: ${err.message}` }],
139
- isError: true,
179
+ const { getPort, close } = await startHttpServer({
180
+ host,
181
+ port,
182
+ buildServer,
183
+ expectedToken: token,
184
+ onShutdown: shutdown,
185
+ });
186
+ const summary = {
187
+ transport: "http",
188
+ port: getPort(),
189
+ backends: backends.list().length,
190
+ favorites: backends.favorites().length,
140
191
  };
192
+ console.log(`[mcp-lazy-proxy] ${JSON.stringify(summary, null, 2)}`);
193
+ process.on("SIGINT", async () => {
194
+ await close();
195
+ process.exit(0);
196
+ });
197
+ process.on("SIGTERM", async () => {
198
+ await close();
199
+ process.exit(0);
200
+ });
201
+ return;
141
202
  }
142
- });
143
- // connect over stdio and clean up backends on shutdown.
144
- const transport = new StdioServerTransport();
145
- await server.connect(transport);
146
- // clean up backends on shutdown.
147
- process.on("SIGINT", async () => {
148
- await backends.closeAll();
149
- process.exit(0);
150
- });
151
- // clean up backends on termination.
152
- process.on("SIGTERM", async () => {
153
- await backends.closeAll();
154
- process.exit(0);
155
- });
203
+ // stdio mode: connect over stdio and clean up backends on shutdown.
204
+ const server = buildServer();
205
+ const transportStream = new StdioServerTransport();
206
+ await server.connect(transportStream);
207
+ const summary = {
208
+ transport: "stdio",
209
+ backends: backends.list().length,
210
+ favorites: backends.favorites().length,
211
+ };
212
+ console.error(`[mcp-lazy-proxy] ${JSON.stringify(summary, null, 2)}`);
213
+ process.on("SIGINT", async () => {
214
+ await shutdown();
215
+ process.exit(0);
216
+ });
217
+ process.on("SIGTERM", async () => {
218
+ await shutdown();
219
+ process.exit(0);
220
+ });
221
+ return undefined;
222
+ }
223
+ await main();
156
224
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACnG,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEhE,MAAM,UAAU,GAAG,UAAU,CAAC;AAC9B,MAAM,aAAa,GAAG,aAAa,CAAC;AACpC,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAC1B,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAC1B,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAE5C,4EAA4E;AAC5E,6EAA6E;AAC7E,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,OAAO,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,OAAO,CAAC,CAAC;AACX,CAAC;AAUD,yEAAyE;AACzE,SAAS,SAAS,CAAC,IAAc,EAAE,IAAY;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACpE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,uEAAuE;AACvE,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,UAAU,GACd,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,mBAAmB,CAAC,CAAC;IAChG,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,OAAO,CAAyB,CAAC;IACtF,IAAI,SAAS,KAAK,OAAO,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,4BAA4B,SAAS,gCAAgC,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChG,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,SAAS,CAAC;IACpD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,yDAAyD;IACzD,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAEvD,8EAA8E;IAC9E,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,MAAM,QAAQ,CAAC,aAAa,EAAE,CAAC;IAEtF,kDAAkD;IAClD,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC;IAEF,iEAAiE;IACjE,yEAAyE;IACzE,8EAA8E;IAC9E,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAM,WAAW,GAAG,GAAG,EAAE;QACvB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,EACxD;YACE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YAC3B,YAAY,EAAE;gBACZ,sGAAsG;gBACtG,qBAAqB;gBACrB,GAAG,QAAQ;qBACR,IAAI,EAAE;qBACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,IAAI,gBAAgB,EAAE,CAAC;gBAClE,kGAAkG;aACnG,CAAC,IAAI,CAAC,IAAI,CAAC;SACb,CACF,CAAC;QAEF,6EAA6E;QAC7E,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YAC5D,KAAK,EAAE;gBACL,GAAG,aAAa;gBAChB;oBACE,IAAI,EAAE,eAAe;oBACrB,WAAW,EACT,wOAAwO;oBAC1O,WAAW,EAAE;wBACX,IAAI,EAAE,QAAQ;wBACd,UAAU,EAAE;4BACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gDAAgD,EAAE;4BACtF,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qDAAqD,EAAE;4BAC7F,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uCAAuC,EAAE;yBAChF;wBACD,QAAQ,EAAE,CAAC,KAAK,CAAC;qBAClB;iBACF;gBACD;oBACE,IAAI,EAAE,eAAe;oBACrB,WAAW,EACT,uGAAuG;oBACzG,WAAW,EAAE;wBACX,IAAI,EAAE,QAAQ;wBACd,UAAU,EAAE;4BACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gDAAgD,EAAE;4BACtF,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yCAAyC,EAAE;4BAChF,SAAS,EAAE;gCACT,IAAI,EAAE,QAAQ;gCACd,WAAW,EAAE,wCAAwC;gCACrD,oBAAoB,EAAE,IAAI;6BAC3B;yBACF;wBACD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC;qBACvC;iBACF;aACF;SACF,CAAC,CAAC,CAAC;QAEJ,wEAAwE;QACxE,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC9D,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAC7C,IAAI,CAAC;gBACH,6EAA6E;gBAC7E,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;oBAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;oBACpC,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;oBAC3D,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC5D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACvC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;oBACnD,OAAO;wBACL,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI,EAAE,KAAK,EAAE,EACvD,IAAI,EACJ,CAAC,CACF;6BACF;yBACF;qBACF,CAAC;gBACJ,CAAC;gBAED,yDAAyD;gBACzD,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;oBAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;oBACpC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;oBACtC,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,CAA4B,CAAC;oBACpE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACvC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;oBACrD,OAAO;wBACL,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;6BAC5E;yBACF;qBACF,CAAC;gBACJ,CAAC;gBAED,yEAAyE;gBACzE,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACzC,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;oBACpD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC,CAAC;oBACrG,OAAO;wBACL,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;6BAC5E;yBACF;qBACF,CAAC;gBACJ,CAAC;gBAED,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YACvF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC;oBACrE,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,oCAAoC;IACpC,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,kEAAkE;QAClE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QACzC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,qGAAqG,CACtG,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,eAAe,CAAC;YAC/C,IAAI;YACJ,IAAI;YACJ,WAAW;YACX,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,QAAQ;SACrB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG;YACd,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO,EAAE;YACf,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM;YAChC,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM;SACvC,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAEpE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC9B,MAAM,KAAK,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;YAC/B,MAAM,KAAK,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,oEAAoE;IACpE,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;IAC7B,MAAM,eAAe,GAAG,IAAI,oBAAoB,EAAE,CAAC;IACnD,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAEtC,MAAM,OAAO,GAAG;QACd,SAAS,EAAE,OAAO;QAClB,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM;QAChC,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM;KACvC,CAAC;IACF,OAAO,CAAC,KAAK,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAEtE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,QAAQ,EAAE,CAAC;QACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,QAAQ,EAAE,CAAC;QACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,IAAI,EAAE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { Server as NodeHttpServer } from "node:http";
2
+ import type { Server as McpServer } from "@modelcontextprotocol/sdk/server/index.js";
3
+ export interface HttpServerOptions {
4
+ host: string;
5
+ port: number;
6
+ /** builds a fresh MCP Server for each client session (one transport per Server). */
7
+ buildServer: () => McpServer;
8
+ /** expected bearer token; when provided, requests without it return 401. */
9
+ expectedToken?: string;
10
+ /** how long a session may sit idle before it is closed and evicted (ms). 0 disables. */
11
+ sessionIdleTimeoutMs?: number;
12
+ /** called on graceful shutdown to close backends. */
13
+ onShutdown?: () => Promise<void>;
14
+ }
15
+ export declare function startHttpServer(opts: HttpServerOptions): Promise<{
16
+ server: NodeHttpServer;
17
+ getPort: () => number;
18
+ close: () => Promise<void>;
19
+ }>;
package/dist/server.js ADDED
@@ -0,0 +1,109 @@
1
+ import { createServer as createNodeHttpServer } from "node:http";
2
+ import { randomUUID, timingSafeEqual } from "node:crypto";
3
+ import { StreamableHTTPServerTransport, } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ // timing-safe token comparison. uses a length check first because
5
+ // crypto.timingSafeEqual throws on buffers of differing lengths, then delegates
6
+ // to the native constant-time comparison.
7
+ function safeEqual(a, b) {
8
+ if (a.length !== b.length)
9
+ return false;
10
+ return timingSafeEqual(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"));
11
+ }
12
+ // parse the "Authorization: Bearer <token>" header; returns the bearer token
13
+ // or undefined if the header is missing/unparseable.
14
+ function bearerToken(authHeader) {
15
+ if (!authHeader)
16
+ return undefined;
17
+ const m = /^Bearer\s+(.+)$/i.exec(authHeader.trim());
18
+ return m ? m[1] : undefined;
19
+ }
20
+ // start a Streamable HTTP MCP server on a single endpoint (POST /mcp).
21
+ // clients get their own Server+transport per streamable session, so the proxy
22
+ // can serve many concurrent clients while reusing the shared backend manager.
23
+ export async function startHttpServer(opts) {
24
+ const { buildServer, expectedToken, onShutdown } = opts;
25
+ const sessionIdleTimeoutMs = opts.sessionIdleTimeoutMs ?? 5 * 60_000;
26
+ const sessions = new Map();
27
+ // evict and close a single session, removing it from the map and stopping
28
+ // its transport so the underlying SSE stream is torn down.
29
+ const evictSession = async (sid) => {
30
+ const entry = sessions.get(sid);
31
+ if (!entry)
32
+ return;
33
+ sessions.delete(sid);
34
+ await entry.transport.close().catch(() => { });
35
+ await entry.server.close().catch(() => { });
36
+ };
37
+ const nodeServer = createNodeHttpServer(async (req, res) => {
38
+ // health probe: always allowed, no auth, so container HEALTHCHECK can probe.
39
+ if (req.method === "GET" && req.url === "/health") {
40
+ res.writeHead(200, { "Content-Type": "application/json" });
41
+ res.end(JSON.stringify({ ok: true }));
42
+ return;
43
+ }
44
+ // auth gate: reject anything except a valid bearer token when one is set.
45
+ if (expectedToken) {
46
+ const token = bearerToken(req.headers.authorization);
47
+ if (!token || !safeEqual(token, expectedToken)) {
48
+ res.writeHead(401, { "Content-Type": "application/json" });
49
+ res.end(JSON.stringify({ error: "unauthorized" }));
50
+ return;
51
+ }
52
+ }
53
+ // path gate: the MCP endpoint is POST /mcp; everything else is not found.
54
+ if (req.method === "POST" && req.url === "/mcp") {
55
+ const sessionId = req.headers["mcp-session-id"];
56
+ // resume an existing session via its transport
57
+ if (sessionId && sessions.has(sessionId)) {
58
+ const entry = sessions.get(sessionId);
59
+ entry.lastActivity = Date.now();
60
+ await entry.transport.handleRequest(req, res);
61
+ return;
62
+ }
63
+ // new streamable session: create a fresh Server + transport
64
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
65
+ const sessionServer = buildServer();
66
+ await sessionServer.connect(transport);
67
+ // handleRequest populates transport.sessionId for a brand-new session;
68
+ // register it afterwards so subsequent requests can be routed back.
69
+ await transport.handleRequest(req, res);
70
+ if (transport.sessionId) {
71
+ const sid = transport.sessionId;
72
+ sessions.set(sid, { server: sessionServer, transport, lastActivity: Date.now() });
73
+ transport.onclose = () => {
74
+ sessions.delete(sid);
75
+ void sessionServer.close();
76
+ };
77
+ transport.onerror = (err) => console.error("[http-proxy] transport error:", err);
78
+ }
79
+ return;
80
+ }
81
+ res.writeHead(404, { "Content-Type": "application/json" });
82
+ res.end(JSON.stringify({ error: "not found" }));
83
+ });
84
+ await new Promise((resolve) => nodeServer.listen(opts.port, opts.host, resolve));
85
+ const { port } = nodeServer.address();
86
+ // periodic sweep: close and evict sessions that have been idle too long, so
87
+ // abandoned clients don't accumulate connections/memory on the proxy.
88
+ const idleTimer = sessionIdleTimeoutMs > 0
89
+ ? setInterval(() => {
90
+ const cutoff = Date.now() - sessionIdleTimeoutMs;
91
+ for (const [sid, entry] of sessions) {
92
+ if (entry.lastActivity < cutoff)
93
+ void evictSession(sid);
94
+ }
95
+ }, Math.min(sessionIdleTimeoutMs, 30_000)).unref?.() ?? undefined
96
+ : undefined;
97
+ return {
98
+ server: nodeServer,
99
+ getPort: () => port,
100
+ close: async () => {
101
+ if (idleTimer)
102
+ clearInterval(idleTimer);
103
+ await Promise.all([...sessions.keys()].map((sid) => evictSession(sid)));
104
+ await onShutdown?.();
105
+ await new Promise((resolve, reject) => nodeServer.close((err) => (err ? reject(err) : resolve())));
106
+ },
107
+ };
108
+ }
109
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarthakpranesh/mcp-lazy-proxy",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Lazy MCP proxy: exposes get_mcp_tools / call_mcp_tool meta-tools that load backend MCP servers on demand, so only two tool schemas are injected into the model context.",
5
5
  "type": "module",
6
6
  "bin": {