@sarthakpranesh/mcp-lazy-proxy 0.1.8 → 0.1.10
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/package.json +1 -1
- package/dist/backend.d.ts +0 -40
- package/dist/backend.js +0 -171
- package/dist/backend.js.map +0 -1
- package/dist/config.d.ts +0 -21
- package/dist/config.js +0 -39
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -224
- package/dist/index.js.map +0 -1
- package/dist/server.d.ts +0 -19
- package/dist/server.js +0 -138
- package/dist/server.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sarthakpranesh/mcp-lazy-proxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
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": {
|
package/dist/backend.d.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { BackendConfig } from "./config.js";
|
|
2
|
-
export interface BackendTool {
|
|
3
|
-
name: string;
|
|
4
|
-
description?: string;
|
|
5
|
-
inputSchema?: Record<string, unknown>;
|
|
6
|
-
}
|
|
7
|
-
export interface BackendHandle {
|
|
8
|
-
instruction?: string;
|
|
9
|
-
listTools(query?: string, limit?: number): Promise<BackendTool[]>;
|
|
10
|
-
callTool(tool: string, args: Record<string, unknown>): Promise<unknown>;
|
|
11
|
-
close(): Promise<void>;
|
|
12
|
-
}
|
|
13
|
-
export interface BackendSummary {
|
|
14
|
-
name: string;
|
|
15
|
-
instruction?: string;
|
|
16
|
-
}
|
|
17
|
-
export declare class BackendManager {
|
|
18
|
-
private readonly configs;
|
|
19
|
-
private readonly clients;
|
|
20
|
-
private readonly tools;
|
|
21
|
-
private readonly idleTimers;
|
|
22
|
-
constructor(configs: Record<string, BackendConfig>);
|
|
23
|
-
list(): BackendSummary[];
|
|
24
|
-
favorites(): string[];
|
|
25
|
-
loadFavorites(): Promise<{
|
|
26
|
-
tools: BackendTool[];
|
|
27
|
-
owner: Map<string, {
|
|
28
|
-
backend: string;
|
|
29
|
-
realToolName: string;
|
|
30
|
-
}>;
|
|
31
|
-
}>;
|
|
32
|
-
get(name: string): Promise<BackendHandle>;
|
|
33
|
-
private connect;
|
|
34
|
-
private buildTransport;
|
|
35
|
-
private listTools;
|
|
36
|
-
private callTool;
|
|
37
|
-
private resetIdleTimer;
|
|
38
|
-
private close;
|
|
39
|
-
closeAll(): Promise<void>;
|
|
40
|
-
}
|
package/dist/backend.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
|
-
import packageJson from "../package.json" with { type: "json" };
|
|
5
|
-
// the idle timeout for a backend connection
|
|
6
|
-
const IDLE_TIMEOUT_MS = 5 * 60_000;
|
|
7
|
-
// lazily connects to backends, caches their tool lists, and closes on idle.
|
|
8
|
-
export class BackendManager {
|
|
9
|
-
configs;
|
|
10
|
-
clients = new Map();
|
|
11
|
-
tools = new Map();
|
|
12
|
-
idleTimers = new Map();
|
|
13
|
-
constructor(configs) {
|
|
14
|
-
this.configs = configs;
|
|
15
|
-
}
|
|
16
|
-
// return the catalog of configured backends (name + instruction).
|
|
17
|
-
list() {
|
|
18
|
-
return Object.entries(this.configs).map(([name, cfg]) => ({
|
|
19
|
-
name,
|
|
20
|
-
instruction: cfg.instruction,
|
|
21
|
-
}));
|
|
22
|
-
}
|
|
23
|
-
// return the names of backends flagged as favorites (eagerly loaded).
|
|
24
|
-
favorites() {
|
|
25
|
-
return Object.entries(this.configs)
|
|
26
|
-
.filter(([, cfg]) => cfg.favorite)
|
|
27
|
-
.map(([name]) => name);
|
|
28
|
-
}
|
|
29
|
-
// eagerly connect to the favorite backends and collect their tool schemas,
|
|
30
|
-
// along with a map of each tool name to its owning backend.
|
|
31
|
-
// a backend that fails to load is skipped so one bad favorite can't crash the proxy.
|
|
32
|
-
async loadFavorites() {
|
|
33
|
-
const names = this.favorites();
|
|
34
|
-
const sets = await Promise.all(names.map(async (n) => {
|
|
35
|
-
try {
|
|
36
|
-
return { name: n, tools: await this.listTools(n) };
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return { name: n, tools: [] };
|
|
40
|
-
}
|
|
41
|
-
}));
|
|
42
|
-
const tools = [];
|
|
43
|
-
const owner = new Map();
|
|
44
|
-
for (const s of sets) {
|
|
45
|
-
for (const t of s.tools) {
|
|
46
|
-
// tool names are prefixed with the backend name so same-named tools from
|
|
47
|
-
// different backends (e.g. promptify vs promptify_personal) don't collide.
|
|
48
|
-
// has two underscores to separate the backend name from the tool name
|
|
49
|
-
const prefixed = `${s.name}__${t.name}`;
|
|
50
|
-
tools.push({ ...t, name: prefixed });
|
|
51
|
-
if (!owner.has(prefixed))
|
|
52
|
-
owner.set(prefixed, {
|
|
53
|
-
backend: s.name,
|
|
54
|
-
realToolName: t.name,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return { tools, owner };
|
|
59
|
-
}
|
|
60
|
-
// get a handle to a backend, connecting lazily and resetting its idle timer.
|
|
61
|
-
async get(name) {
|
|
62
|
-
const cfg = this.configs[name];
|
|
63
|
-
if (!cfg) {
|
|
64
|
-
throw new Error(`unknown mcp server "${name}"`);
|
|
65
|
-
}
|
|
66
|
-
const client = await this.connect(name, cfg);
|
|
67
|
-
this.resetIdleTimer(name);
|
|
68
|
-
return {
|
|
69
|
-
instruction: cfg.instruction ?? client.getInstructions(),
|
|
70
|
-
listTools: (query, limit) => this.listTools(name, query, limit),
|
|
71
|
-
callTool: (tool, args) => this.callTool(name, tool, args),
|
|
72
|
-
close: () => this.close(name),
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
// connect to a backend if not already connected, reusing the cached client.
|
|
76
|
-
async connect(name, cfg) {
|
|
77
|
-
const existing = this.clients.get(name);
|
|
78
|
-
if (existing)
|
|
79
|
-
return existing;
|
|
80
|
-
const transport = this.buildTransport(cfg);
|
|
81
|
-
// create a new client with the package name and version
|
|
82
|
-
const client = new Client({ name: packageJson.name, version: packageJson.version });
|
|
83
|
-
await client.connect(transport);
|
|
84
|
-
// cache the client
|
|
85
|
-
this.clients.set(name, client);
|
|
86
|
-
return client;
|
|
87
|
-
}
|
|
88
|
-
// build the SDK transport for a backend (HTTP for url, stdio for command).
|
|
89
|
-
buildTransport(cfg) {
|
|
90
|
-
// build the transport for a remote backend
|
|
91
|
-
if ("url" in cfg && cfg.url) {
|
|
92
|
-
return new StreamableHTTPClientTransport(new URL(cfg.url), {
|
|
93
|
-
requestInit: cfg.headers ? { headers: cfg.headers } : undefined,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
// build the transport for a local backend
|
|
97
|
-
const local = cfg;
|
|
98
|
-
return new StdioClientTransport({
|
|
99
|
-
command: local.command,
|
|
100
|
-
args: local.args ?? [],
|
|
101
|
-
env: local.env,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
// list a backend's tools, caching them and applying optional query/limit filters.
|
|
105
|
-
async listTools(name, query, limit) {
|
|
106
|
-
const client = await this.connect(name, this.configs[name]);
|
|
107
|
-
// get the cached tools for the backend
|
|
108
|
-
let tools = this.tools.get(name);
|
|
109
|
-
if (!tools) {
|
|
110
|
-
const res = await client.listTools();
|
|
111
|
-
tools = (res.tools ?? []).map((t) => ({
|
|
112
|
-
name: t.name,
|
|
113
|
-
description: t.description,
|
|
114
|
-
inputSchema: t.inputSchema,
|
|
115
|
-
}));
|
|
116
|
-
this.tools.set(name, tools);
|
|
117
|
-
}
|
|
118
|
-
// apply the optional query/limit filters
|
|
119
|
-
let filtered = tools;
|
|
120
|
-
if (query) {
|
|
121
|
-
const q = query.toLowerCase();
|
|
122
|
-
filtered = tools.filter((t) => t.name.toLowerCase().includes(q) || (t.description ?? "").toLowerCase().includes(q));
|
|
123
|
-
}
|
|
124
|
-
if (limit && limit > 0) {
|
|
125
|
-
filtered = filtered.slice(0, limit);
|
|
126
|
-
}
|
|
127
|
-
return filtered;
|
|
128
|
-
}
|
|
129
|
-
// forward a tool call to a backend and return its raw result content.
|
|
130
|
-
async callTool(name, tool, args) {
|
|
131
|
-
const client = await this.connect(name, this.configs[name]);
|
|
132
|
-
const res = await client.callTool({ name: tool, arguments: args });
|
|
133
|
-
return res.content ?? res;
|
|
134
|
-
}
|
|
135
|
-
// reset the idle-close timer for a backend on each use.
|
|
136
|
-
// used to keep the backend connection alive for a period of time after the last use.
|
|
137
|
-
resetIdleTimer(name) {
|
|
138
|
-
const existing = this.idleTimers.get(name);
|
|
139
|
-
if (existing)
|
|
140
|
-
clearTimeout(existing);
|
|
141
|
-
const timer = setTimeout(() => void this.close(name), IDLE_TIMEOUT_MS);
|
|
142
|
-
timer.unref?.();
|
|
143
|
-
this.idleTimers.set(name, timer);
|
|
144
|
-
}
|
|
145
|
-
// close a backend's connection and drop its cached tools/timer.
|
|
146
|
-
async close(name) {
|
|
147
|
-
// clear the idle timer
|
|
148
|
-
const timer = this.idleTimers.get(name);
|
|
149
|
-
if (timer)
|
|
150
|
-
clearTimeout(timer);
|
|
151
|
-
this.idleTimers.delete(name);
|
|
152
|
-
// delete the cached tools
|
|
153
|
-
this.tools.delete(name);
|
|
154
|
-
// close the client
|
|
155
|
-
const client = this.clients.get(name);
|
|
156
|
-
if (client) {
|
|
157
|
-
this.clients.delete(name);
|
|
158
|
-
try {
|
|
159
|
-
await client.close();
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
// best-effort: ignore errors closing the client
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// close every connected backend (used on shutdown).
|
|
167
|
-
async closeAll() {
|
|
168
|
-
await Promise.all([...this.clients.keys()].map((n) => this.close(n)));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
//# sourceMappingURL=backend.js.map
|
package/dist/backend.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export interface RemoteBackendConfig {
|
|
2
|
-
type?: "remote";
|
|
3
|
-
url: string;
|
|
4
|
-
headers?: Record<string, string>;
|
|
5
|
-
}
|
|
6
|
-
export interface LocalBackendConfig {
|
|
7
|
-
type: "local";
|
|
8
|
-
command: string;
|
|
9
|
-
args?: string[];
|
|
10
|
-
env?: Record<string, string>;
|
|
11
|
-
}
|
|
12
|
-
export type BackendConfig = (RemoteBackendConfig | LocalBackendConfig) & {
|
|
13
|
-
/** Optional human/LLM-facing description of what this MCP is for. */
|
|
14
|
-
instruction?: string;
|
|
15
|
-
/** Eagerly inject this backend's schemas into the model context instead of keeping it lazy. */
|
|
16
|
-
favorite?: boolean;
|
|
17
|
-
};
|
|
18
|
-
export interface ProxyConfig {
|
|
19
|
-
mcpServers: Record<string, BackendConfig>;
|
|
20
|
-
}
|
|
21
|
-
export declare function loadConfig(path: string): ProxyConfig;
|
package/dist/config.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
// parse and validate the config file, ensuring every server has url or command.
|
|
3
|
-
export function loadConfig(path) {
|
|
4
|
-
const raw = readJson(path);
|
|
5
|
-
const servers = raw.mcpServers;
|
|
6
|
-
if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
|
|
7
|
-
throw new Error(`config "${path}" must have an "mcpServers" object`);
|
|
8
|
-
}
|
|
9
|
-
// validate that each server has a url or command
|
|
10
|
-
for (const [name, cfg] of Object.entries(servers)) {
|
|
11
|
-
if (!cfg || typeof cfg !== "object") {
|
|
12
|
-
throw new Error(`mcp server "${name}" must be an object`);
|
|
13
|
-
}
|
|
14
|
-
const hasUrl = typeof cfg.url === "string";
|
|
15
|
-
const hasCommand = typeof cfg.command === "string";
|
|
16
|
-
if (!hasUrl && !hasCommand) {
|
|
17
|
-
throw new Error(`mcp server "${name}" must have a "url" or "command"`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return { mcpServers: servers };
|
|
21
|
-
}
|
|
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.
|
|
35
|
-
function readJson(path) {
|
|
36
|
-
const text = readFileSync(path, "utf8");
|
|
37
|
-
return JSON.parse(substituteEnv(text));
|
|
38
|
-
}
|
|
39
|
-
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
-
import { homedir } from "node:os";
|
|
6
|
-
import { loadConfig } from "./config.js";
|
|
7
|
-
import { BackendManager } from "./backend.js";
|
|
8
|
-
import { startHttpServer } from "./server.js";
|
|
9
|
-
import packageJson from "../package.json" with { type: "json" };
|
|
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";
|
|
15
|
-
// expand a leading "~" to the user's home directory, since the proxy may be
|
|
16
|
-
// spawned without a shell (e.g. by an MCP client) where "~" is not expanded.
|
|
17
|
-
function expandHome(p) {
|
|
18
|
-
if (p === "~")
|
|
19
|
-
return homedir();
|
|
20
|
-
if (p.startsWith("~/") || p.startsWith("~\\"))
|
|
21
|
-
return `${homedir()}${p.slice(1)}`;
|
|
22
|
-
return p;
|
|
23
|
-
}
|
|
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.
|
|
35
|
-
function parseArgs(argv) {
|
|
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")`);
|
|
40
|
-
}
|
|
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 };
|
|
44
|
-
}
|
|
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: {
|
|
83
|
-
type: "object",
|
|
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"],
|
|
90
|
-
},
|
|
91
|
-
},
|
|
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"],
|
|
107
|
-
},
|
|
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.");
|
|
178
|
-
}
|
|
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,
|
|
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;
|
|
202
|
-
}
|
|
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();
|
|
224
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
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; // 5 minutes
|
|
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
|
-
// log each request and its resulting status once the response is finished.
|
|
38
|
-
const logRequest = (req, res, label) => {
|
|
39
|
-
const sessionId = req.headers["mcp-session-id"];
|
|
40
|
-
const at = new Date().toISOString();
|
|
41
|
-
const onFinish = () => {
|
|
42
|
-
res.removeListener("finish", onFinish);
|
|
43
|
-
console.log(`[http] ${at} ${req.method} ${req.url} ${res.statusCode} ${sessionId ? `session=${sessionId}` : ""} ${label}`.trim());
|
|
44
|
-
};
|
|
45
|
-
res.on("finish", onFinish);
|
|
46
|
-
};
|
|
47
|
-
const nodeServer = createNodeHttpServer(async (req, res) => {
|
|
48
|
-
logRequest(req, res, "");
|
|
49
|
-
// health probe: always allowed, no auth, so container HEALTHCHECK can probe.
|
|
50
|
-
if (req.method === "GET" && req.url === "/health") {
|
|
51
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
52
|
-
res.end(JSON.stringify({ ok: true }));
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
// auth gate: reject anything except a valid bearer token when one is set.
|
|
56
|
-
if (expectedToken) {
|
|
57
|
-
const token = bearerToken(req.headers.authorization);
|
|
58
|
-
if (!token || !safeEqual(token, expectedToken)) {
|
|
59
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
60
|
-
res.end(JSON.stringify({ error: "unauthorized" }));
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
// path gate: the MCP endpoint is /mcp (POST initialize + messages, GET for
|
|
65
|
-
// the SSE stream, DELETE to close). anything else is not found.
|
|
66
|
-
if (req.url === "/mcp") {
|
|
67
|
-
const sessionId = req.headers["mcp-session-id"];
|
|
68
|
-
// a request carrying a session id for a session we don't know about
|
|
69
|
-
// (evicted by idle timeout, or lost on a restart) must be rejected so the
|
|
70
|
-
// client tears down and issues a fresh initialize. silently creating an
|
|
71
|
-
// uninitialized transport here would forward the stale request into the
|
|
72
|
-
// SDK's session validation, which 400-loops with "Server not initialized".
|
|
73
|
-
if (sessionId) {
|
|
74
|
-
const entry = sessions.get(sessionId);
|
|
75
|
-
if (!entry) {
|
|
76
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
77
|
-
res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Session not found" }, id: null }));
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
// resume an existing session via its transport (POST message, GET SSE stream, DELETE close)
|
|
81
|
-
entry.lastActivity = Date.now();
|
|
82
|
-
await entry.transport.handleRequest(req, res);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
// a GET/DELETE without a session id has no target session; the standalone
|
|
86
|
-
// transport isn't used here, so nothing to open or close.
|
|
87
|
-
if (req.method !== "POST") {
|
|
88
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
89
|
-
res.end(JSON.stringify({ error: "not found" }));
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
// new streamable session: create a fresh Server + transport
|
|
93
|
-
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
|
|
94
|
-
const sessionServer = buildServer();
|
|
95
|
-
await sessionServer.connect(transport);
|
|
96
|
-
// handleRequest populates transport.sessionId for a brand-new session;
|
|
97
|
-
// register it afterwards so subsequent requests can be routed back.
|
|
98
|
-
await transport.handleRequest(req, res);
|
|
99
|
-
if (transport.sessionId) {
|
|
100
|
-
const sid = transport.sessionId;
|
|
101
|
-
sessions.set(sid, { server: sessionServer, transport, lastActivity: Date.now() });
|
|
102
|
-
transport.onclose = () => {
|
|
103
|
-
sessions.delete(sid);
|
|
104
|
-
void sessionServer.close();
|
|
105
|
-
};
|
|
106
|
-
transport.onerror = (err) => console.error("[http-proxy] transport error:", err);
|
|
107
|
-
}
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
111
|
-
res.end(JSON.stringify({ error: "not found" }));
|
|
112
|
-
});
|
|
113
|
-
await new Promise((resolve) => nodeServer.listen(opts.port, opts.host, resolve));
|
|
114
|
-
const { port } = nodeServer.address();
|
|
115
|
-
// periodic sweep: close and evict sessions that have been idle too long, so
|
|
116
|
-
// abandoned clients don't accumulate connections/memory on the proxy.
|
|
117
|
-
const idleTimer = sessionIdleTimeoutMs > 0
|
|
118
|
-
? setInterval(() => {
|
|
119
|
-
const cutoff = Date.now() - sessionIdleTimeoutMs;
|
|
120
|
-
for (const [sid, entry] of sessions) {
|
|
121
|
-
if (entry.lastActivity < cutoff)
|
|
122
|
-
void evictSession(sid);
|
|
123
|
-
}
|
|
124
|
-
}, Math.min(sessionIdleTimeoutMs, 30_000)).unref?.() ?? undefined
|
|
125
|
-
: undefined;
|
|
126
|
-
return {
|
|
127
|
-
server: nodeServer,
|
|
128
|
-
getPort: () => port,
|
|
129
|
-
close: async () => {
|
|
130
|
-
if (idleTimer)
|
|
131
|
-
clearInterval(idleTimer);
|
|
132
|
-
await Promise.all([...sessions.keys()].map((sid) => evictSession(sid)));
|
|
133
|
-
await onShutdown?.();
|
|
134
|
-
await new Promise((resolve, reject) => nodeServer.close((err) => (err ? reject(err) : resolve())));
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|