mcp-compress-router 1.0.0 → 1.0.2
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 +5 -3
- package/build/catalog.js +0 -54
- package/build/cli/config-path-note.js +0 -21
- package/build/config.js +0 -80
- package/build/discovery.js +0 -39
- package/build/text-format.js +0 -24
- package/build/types.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-compress-router",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Compress all connected MCP servers into a single router MCP to save tokens",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
"mcp-compress-router": "./build/index.js"
|
|
10
10
|
},
|
|
11
11
|
"engines": {
|
|
12
|
-
"node": ">=
|
|
12
|
+
"node": ">=24",
|
|
13
|
+
"pnpm": ">=10"
|
|
13
14
|
},
|
|
15
|
+
"packageManager": "pnpm@10.14.0",
|
|
14
16
|
"publishConfig": {
|
|
15
17
|
"access": "public"
|
|
16
18
|
},
|
|
@@ -50,7 +52,7 @@
|
|
|
50
52
|
"author": "Andrey Meshkov <am@adguard.com>",
|
|
51
53
|
"devDependencies": {
|
|
52
54
|
"@eslint/js": "9.16.0",
|
|
53
|
-
"@types/node": "
|
|
55
|
+
"@types/node": "24.13.2",
|
|
54
56
|
"eslint": "9.16.0",
|
|
55
57
|
"husky": "9.1.7",
|
|
56
58
|
"knip": "6.6.2",
|
package/build/catalog.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds the immutable tool catalog from discovered server data.
|
|
3
|
-
*
|
|
4
|
-
* @param discovered - Results from parallel discovery.
|
|
5
|
-
* @returns An immutable ToolCatalog.
|
|
6
|
-
*/
|
|
7
|
-
export function buildCatalog(discovered) {
|
|
8
|
-
const toolMap = new Map();
|
|
9
|
-
const servers = discovered.map((ds) => {
|
|
10
|
-
for (const tool of ds.tools) {
|
|
11
|
-
const key = `${ds.name}::${tool.name}`;
|
|
12
|
-
toolMap.set(key, tool);
|
|
13
|
-
}
|
|
14
|
-
return {
|
|
15
|
-
name: ds.name,
|
|
16
|
-
description: ds.description,
|
|
17
|
-
tools: ds.tools,
|
|
18
|
-
};
|
|
19
|
-
});
|
|
20
|
-
return { servers, toolMap };
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Looks up tool schemas in the catalog by server and tool names.
|
|
24
|
-
*
|
|
25
|
-
* @param catalog - The tool catalog.
|
|
26
|
-
* @param serverName - The server to look up.
|
|
27
|
-
* @param toolNames - The tool names to retrieve.
|
|
28
|
-
* @returns The matching tool descriptors.
|
|
29
|
-
* @throws If the server or any tool is not found.
|
|
30
|
-
*/
|
|
31
|
-
export function lookupTools(catalog, serverName, toolNames) {
|
|
32
|
-
const server = catalog.servers.find((s) => s.name === serverName);
|
|
33
|
-
if (!server) {
|
|
34
|
-
const available = catalog.servers.map((s) => s.name).join(', ');
|
|
35
|
-
throw new Error(`Server "${serverName}" not found. Available servers: ${available}`);
|
|
36
|
-
}
|
|
37
|
-
const results = [];
|
|
38
|
-
const missing = [];
|
|
39
|
-
for (const toolName of toolNames) {
|
|
40
|
-
const key = `${serverName}::${toolName}`;
|
|
41
|
-
const tool = catalog.toolMap.get(key);
|
|
42
|
-
if (!tool) {
|
|
43
|
-
missing.push(toolName);
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
results.push(tool);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (missing.length > 0) {
|
|
50
|
-
const valid = server.tools.map((t) => t.name).join(', ');
|
|
51
|
-
throw new Error(`Tool(s) not found on server "${serverName}": ${missing.join(', ')}. Valid tools: ${valid}`);
|
|
52
|
-
}
|
|
53
|
-
return results;
|
|
54
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Appends the resolved config file path to a command's output when no
|
|
3
|
-
* explicit `-c`/`--config` flag was provided, so the user can see which
|
|
4
|
-
* default configuration file was operated on.
|
|
5
|
-
*
|
|
6
|
-
* When an explicit path was given via `-c`, the output is returned
|
|
7
|
-
* unchanged.
|
|
8
|
-
*
|
|
9
|
-
* @param result - The original command output (may be empty or undefined).
|
|
10
|
-
* @param explicitConfig - The raw `-c` value, or undefined when omitted.
|
|
11
|
-
* @param resolvedPath - The config file path that was actually used.
|
|
12
|
-
* @returns The output followed by a default-path note when `-c` was
|
|
13
|
-
* omitted; otherwise the original output unchanged.
|
|
14
|
-
*/
|
|
15
|
-
export function appendConfigPathNote(result, explicitConfig, resolvedPath) {
|
|
16
|
-
if (explicitConfig !== undefined) {
|
|
17
|
-
return result;
|
|
18
|
-
}
|
|
19
|
-
const note = `Using config: ${resolvedPath}`;
|
|
20
|
-
return result ? `${result}\n${note}` : note;
|
|
21
|
-
}
|
package/build/config.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import * as path from 'node:path';
|
|
2
|
-
import * as os from 'node:os';
|
|
3
|
-
import * as fs from 'node:fs/promises';
|
|
4
|
-
/**
|
|
5
|
-
* Resolves the configuration file path.
|
|
6
|
-
*
|
|
7
|
-
* Priority: (1) explicit path argument, (2) MCP_COMPRESS_ROUTER_HOME env var,
|
|
8
|
-
* (3) default ~/.local/share/mcp-compress-router/mcp.json.
|
|
9
|
-
*
|
|
10
|
-
* @param explicitPath - An explicit config path, or undefined.
|
|
11
|
-
* @returns The resolved absolute path to the config file.
|
|
12
|
-
*/
|
|
13
|
-
export function resolveConfigPath(explicitPath) {
|
|
14
|
-
if (explicitPath !== undefined) {
|
|
15
|
-
return explicitPath;
|
|
16
|
-
}
|
|
17
|
-
const home = process.env.MCP_COMPRESS_ROUTER_HOME ??
|
|
18
|
-
path.join(os.homedir(), '.local', 'share', 'mcp-compress-router');
|
|
19
|
-
return path.join(home, 'mcp.json');
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Loads and validates the MCP configuration file.
|
|
23
|
-
*
|
|
24
|
-
* Reads a JSON file with a top-level mcpServers object, extracts stdio
|
|
25
|
-
* entries, and validates uniqueness. HTTP servers and variable expansion
|
|
26
|
-
* are deferred to later slices.
|
|
27
|
-
*
|
|
28
|
-
* @param configPath - Absolute path to the mcp.json file.
|
|
29
|
-
* @returns An array of validated DownstreamServerConfig objects.
|
|
30
|
-
* @throws If the file is missing, invalid JSON, or contains duplicate names.
|
|
31
|
-
*/
|
|
32
|
-
export async function loadConfig(configPath) {
|
|
33
|
-
const raw = await fs.readFile(configPath, 'utf-8');
|
|
34
|
-
let parsed;
|
|
35
|
-
try {
|
|
36
|
-
parsed = JSON.parse(raw);
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
throw new Error(`Failed to parse config file: ${configPath}`);
|
|
40
|
-
}
|
|
41
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
42
|
-
throw new Error('Config file must contain a JSON object with mcpServers');
|
|
43
|
-
}
|
|
44
|
-
const obj = parsed;
|
|
45
|
-
const mcpServers = obj.mcpServers;
|
|
46
|
-
if (typeof mcpServers !== 'object' || mcpServers === null) {
|
|
47
|
-
throw new Error('Config file must contain an mcpServers object');
|
|
48
|
-
}
|
|
49
|
-
const names = new Set();
|
|
50
|
-
const servers = [];
|
|
51
|
-
for (const [name, entry] of Object.entries(mcpServers)) {
|
|
52
|
-
if (typeof entry !== 'object' || entry === null) {
|
|
53
|
-
throw new Error(`Server "${name}" must be an object`);
|
|
54
|
-
}
|
|
55
|
-
const server = entry;
|
|
56
|
-
if (server.type !== 'stdio') {
|
|
57
|
-
throw new Error(`Server "${name}" has unsupported type "${String(server.type)}"; only "stdio" is supported`);
|
|
58
|
-
}
|
|
59
|
-
if (typeof server.command !== 'string' || server.command.length === 0) {
|
|
60
|
-
throw new Error(`Server "${name}" is missing required "command" field`);
|
|
61
|
-
}
|
|
62
|
-
if (names.has(name)) {
|
|
63
|
-
throw new Error(`Duplicate server name: "${name}"`);
|
|
64
|
-
}
|
|
65
|
-
names.add(name);
|
|
66
|
-
const args = Array.isArray(server.args) ? server.args.map(String) : undefined;
|
|
67
|
-
const env = server.env !== undefined && typeof server.env === 'object'
|
|
68
|
-
? Object.fromEntries(Object.entries(server.env).map(([k, v]) => [k, String(v)]))
|
|
69
|
-
: undefined;
|
|
70
|
-
const description = typeof server.description === 'string' ? server.description : undefined;
|
|
71
|
-
servers.push({
|
|
72
|
-
name,
|
|
73
|
-
command: server.command,
|
|
74
|
-
args,
|
|
75
|
-
env,
|
|
76
|
-
description,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
return servers;
|
|
80
|
-
}
|
package/build/discovery.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
-
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
-
/**
|
|
4
|
-
* Connects to all configured stdio servers in parallel and discovers
|
|
5
|
-
* their tools. Fails fast if any server is unreachable.
|
|
6
|
-
*
|
|
7
|
-
* @param servers - Validated downstream server configs.
|
|
8
|
-
* @returns Discovered server data for each server.
|
|
9
|
-
* @throws If any server cannot be connected or tools cannot be listed.
|
|
10
|
-
*/
|
|
11
|
-
export async function connectAndDiscover(servers) {
|
|
12
|
-
const results = await Promise.all(servers.map(async (server) => {
|
|
13
|
-
const client = new Client({ name: 'mcp-compress-router', version: '1.0.0' }, { capabilities: {} });
|
|
14
|
-
const transport = new StdioClientTransport({
|
|
15
|
-
command: server.command,
|
|
16
|
-
args: server.args,
|
|
17
|
-
env: server.env,
|
|
18
|
-
});
|
|
19
|
-
try {
|
|
20
|
-
await client.connect(transport);
|
|
21
|
-
const listResult = await client.listTools();
|
|
22
|
-
const tools = listResult.tools.map((t) => ({
|
|
23
|
-
name: t.name,
|
|
24
|
-
description: t.description,
|
|
25
|
-
inputSchema: t.inputSchema,
|
|
26
|
-
}));
|
|
27
|
-
return {
|
|
28
|
-
name: server.name,
|
|
29
|
-
description: server.description,
|
|
30
|
-
tools,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
35
|
-
throw new Error(`Failed to connect to server "${server.name}": ${message}`);
|
|
36
|
-
}
|
|
37
|
-
}));
|
|
38
|
-
return results;
|
|
39
|
-
}
|
package/build/text-format.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Renders the compact catalog as Markdown text suitable for inclusion
|
|
3
|
-
* in the `get_tool_schema` tool description.
|
|
4
|
-
*
|
|
5
|
-
* Format:
|
|
6
|
-
* ## {server name}
|
|
7
|
-
* {description (optional)}
|
|
8
|
-
* {tool1}, {tool2}, ...
|
|
9
|
-
*
|
|
10
|
-
* @param servers - The catalog server entries.
|
|
11
|
-
* @returns Compact catalog text.
|
|
12
|
-
*/
|
|
13
|
-
export function renderCompactCatalog(servers) {
|
|
14
|
-
const lines = [];
|
|
15
|
-
for (const server of servers) {
|
|
16
|
-
lines.push(`## ${server.name}`);
|
|
17
|
-
if (server.description) {
|
|
18
|
-
lines.push(server.description);
|
|
19
|
-
}
|
|
20
|
-
lines.push(server.tools.map((t) => t.name).join(', '));
|
|
21
|
-
lines.push(''); // blank line between servers
|
|
22
|
-
}
|
|
23
|
-
return lines.join('\n').trimEnd();
|
|
24
|
-
}
|
package/build/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|