universal-agent-memory 1.0.18 → 1.0.20
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/dist/benchmarks/benchmark.d.ts +2 -2
- package/dist/bin/cli.js +30 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/mcp-router.d.ts +16 -0
- package/dist/cli/mcp-router.d.ts.map +1 -0
- package/dist/cli/mcp-router.js +143 -0
- package/dist/cli/mcp-router.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-router/config/parser.d.ts +9 -0
- package/dist/mcp-router/config/parser.d.ts.map +1 -0
- package/dist/mcp-router/config/parser.js +137 -0
- package/dist/mcp-router/config/parser.js.map +1 -0
- package/dist/mcp-router/executor/client.d.ts +31 -0
- package/dist/mcp-router/executor/client.d.ts.map +1 -0
- package/dist/mcp-router/executor/client.js +187 -0
- package/dist/mcp-router/executor/client.js.map +1 -0
- package/dist/mcp-router/index.d.ts +18 -0
- package/dist/mcp-router/index.d.ts.map +1 -0
- package/dist/mcp-router/index.js +16 -0
- package/dist/mcp-router/index.js.map +1 -0
- package/dist/mcp-router/search/fuzzy.d.ts +26 -0
- package/dist/mcp-router/search/fuzzy.d.ts.map +1 -0
- package/dist/mcp-router/search/fuzzy.js +94 -0
- package/dist/mcp-router/search/fuzzy.js.map +1 -0
- package/dist/mcp-router/server.d.ts +50 -0
- package/dist/mcp-router/server.d.ts.map +1 -0
- package/dist/mcp-router/server.js +229 -0
- package/dist/mcp-router/server.js.map +1 -0
- package/dist/mcp-router/tools/discover.d.ts +37 -0
- package/dist/mcp-router/tools/discover.d.ts.map +1 -0
- package/dist/mcp-router/tools/discover.js +65 -0
- package/dist/mcp-router/tools/discover.js.map +1 -0
- package/dist/mcp-router/tools/execute.d.ts +38 -0
- package/dist/mcp-router/tools/execute.d.ts.map +1 -0
- package/dist/mcp-router/tools/execute.js +92 -0
- package/dist/mcp-router/tools/execute.js.map +1 -0
- package/dist/mcp-router/types.d.ts +54 -0
- package/dist/mcp-router/types.d.ts.map +1 -0
- package/dist/mcp-router/types.js +6 -0
- package/dist/mcp-router/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Client Executor
|
|
3
|
+
* Spawns and communicates with MCP servers
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
export class McpClient {
|
|
7
|
+
process = null;
|
|
8
|
+
requestId = 0;
|
|
9
|
+
pendingRequests = new Map();
|
|
10
|
+
buffer = '';
|
|
11
|
+
initialized = false;
|
|
12
|
+
serverName;
|
|
13
|
+
config;
|
|
14
|
+
constructor(serverName, config) {
|
|
15
|
+
this.serverName = serverName;
|
|
16
|
+
this.config = config;
|
|
17
|
+
}
|
|
18
|
+
async connect() {
|
|
19
|
+
if (this.process)
|
|
20
|
+
return;
|
|
21
|
+
if (this.config.url) {
|
|
22
|
+
// HTTP/SSE transport - not implemented in lightweight version
|
|
23
|
+
throw new Error(`HTTP transport not yet supported for ${this.serverName}`);
|
|
24
|
+
}
|
|
25
|
+
if (!this.config.command) {
|
|
26
|
+
throw new Error(`No command specified for server ${this.serverName}`);
|
|
27
|
+
}
|
|
28
|
+
const env = { ...process.env, ...this.config.env };
|
|
29
|
+
this.process = spawn(this.config.command, this.config.args || [], {
|
|
30
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
31
|
+
env,
|
|
32
|
+
});
|
|
33
|
+
this.process.stdout?.on('data', (data) => {
|
|
34
|
+
this.handleData(data.toString());
|
|
35
|
+
});
|
|
36
|
+
this.process.stderr?.on('data', (data) => {
|
|
37
|
+
// Log stderr but don't fail
|
|
38
|
+
console.error(`[${this.serverName}] stderr:`, data.toString());
|
|
39
|
+
});
|
|
40
|
+
this.process.on('error', (err) => {
|
|
41
|
+
console.error(`[${this.serverName}] process error:`, err);
|
|
42
|
+
this.cleanup();
|
|
43
|
+
});
|
|
44
|
+
this.process.on('exit', (code) => {
|
|
45
|
+
if (code !== 0) {
|
|
46
|
+
console.error(`[${this.serverName}] exited with code ${code}`);
|
|
47
|
+
}
|
|
48
|
+
this.cleanup();
|
|
49
|
+
});
|
|
50
|
+
// Initialize MCP connection
|
|
51
|
+
await this.initialize();
|
|
52
|
+
}
|
|
53
|
+
handleData(data) {
|
|
54
|
+
this.buffer += data;
|
|
55
|
+
// Process complete JSON-RPC messages (newline-delimited)
|
|
56
|
+
const lines = this.buffer.split('\n');
|
|
57
|
+
this.buffer = lines.pop() || '';
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
if (!line.trim())
|
|
60
|
+
continue;
|
|
61
|
+
try {
|
|
62
|
+
const response = JSON.parse(line);
|
|
63
|
+
const pending = this.pendingRequests.get(response.id);
|
|
64
|
+
if (pending) {
|
|
65
|
+
this.pendingRequests.delete(response.id);
|
|
66
|
+
if (response.error) {
|
|
67
|
+
pending.reject(new Error(response.error.message));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
pending.resolve(response.result);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
// Ignore non-JSON lines
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async sendRequest(method, params) {
|
|
80
|
+
if (!this.process?.stdin) {
|
|
81
|
+
throw new Error(`Not connected to ${this.serverName}`);
|
|
82
|
+
}
|
|
83
|
+
const id = ++this.requestId;
|
|
84
|
+
const request = {
|
|
85
|
+
jsonrpc: '2.0',
|
|
86
|
+
id,
|
|
87
|
+
method,
|
|
88
|
+
params,
|
|
89
|
+
};
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
92
|
+
const timeout = setTimeout(() => {
|
|
93
|
+
this.pendingRequests.delete(id);
|
|
94
|
+
reject(new Error(`Request timeout for ${method}`));
|
|
95
|
+
}, 30000);
|
|
96
|
+
const originalResolve = this.pendingRequests.get(id).resolve;
|
|
97
|
+
this.pendingRequests.get(id).resolve = (value) => {
|
|
98
|
+
clearTimeout(timeout);
|
|
99
|
+
originalResolve(value);
|
|
100
|
+
};
|
|
101
|
+
this.process.stdin.write(JSON.stringify(request) + '\n');
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async initialize() {
|
|
105
|
+
await this.sendRequest('initialize', {
|
|
106
|
+
protocolVersion: '2024-11-05',
|
|
107
|
+
capabilities: {},
|
|
108
|
+
clientInfo: {
|
|
109
|
+
name: 'uam-mcp-router',
|
|
110
|
+
version: '1.0.0',
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
// Send initialized notification
|
|
114
|
+
this.process?.stdin?.write(JSON.stringify({
|
|
115
|
+
jsonrpc: '2.0',
|
|
116
|
+
method: 'notifications/initialized',
|
|
117
|
+
}) + '\n');
|
|
118
|
+
this.initialized = true;
|
|
119
|
+
}
|
|
120
|
+
async listTools() {
|
|
121
|
+
if (!this.initialized) {
|
|
122
|
+
await this.connect();
|
|
123
|
+
}
|
|
124
|
+
const result = await this.sendRequest('tools/list');
|
|
125
|
+
return (result.tools || []).map(tool => ({
|
|
126
|
+
name: tool.name,
|
|
127
|
+
description: tool.description || '',
|
|
128
|
+
inputSchema: tool.inputSchema,
|
|
129
|
+
serverName: this.serverName,
|
|
130
|
+
serverConfig: this.config,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
async callTool(toolName, args = {}) {
|
|
134
|
+
if (!this.initialized) {
|
|
135
|
+
await this.connect();
|
|
136
|
+
}
|
|
137
|
+
const result = await this.sendRequest('tools/call', {
|
|
138
|
+
name: toolName,
|
|
139
|
+
arguments: args,
|
|
140
|
+
});
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
cleanup() {
|
|
144
|
+
this.process = null;
|
|
145
|
+
this.initialized = false;
|
|
146
|
+
this.buffer = '';
|
|
147
|
+
// Reject all pending requests
|
|
148
|
+
for (const [_id, { reject }] of this.pendingRequests) {
|
|
149
|
+
reject(new Error('Connection closed'));
|
|
150
|
+
}
|
|
151
|
+
this.pendingRequests.clear();
|
|
152
|
+
}
|
|
153
|
+
disconnect() {
|
|
154
|
+
if (this.process) {
|
|
155
|
+
this.process.stdin?.end();
|
|
156
|
+
this.process.kill();
|
|
157
|
+
this.cleanup();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
get isConnected() {
|
|
161
|
+
return this.process !== null && this.initialized;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Connection pool for reusing MCP clients
|
|
165
|
+
export class McpClientPool {
|
|
166
|
+
clients = new Map();
|
|
167
|
+
getClient(serverName, config) {
|
|
168
|
+
let client = this.clients.get(serverName);
|
|
169
|
+
if (!client) {
|
|
170
|
+
client = new McpClient(serverName, config);
|
|
171
|
+
this.clients.set(serverName, client);
|
|
172
|
+
}
|
|
173
|
+
return client;
|
|
174
|
+
}
|
|
175
|
+
async disconnectAll() {
|
|
176
|
+
for (const client of this.clients.values()) {
|
|
177
|
+
client.disconnect();
|
|
178
|
+
}
|
|
179
|
+
this.clients.clear();
|
|
180
|
+
}
|
|
181
|
+
getConnectedServers() {
|
|
182
|
+
return Array.from(this.clients.entries())
|
|
183
|
+
.filter(([_, client]) => client.isConnected)
|
|
184
|
+
.map(([name]) => name);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/mcp-router/executor/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Router - Lightweight Hierarchical Router for 98%+ Token Reduction
|
|
3
|
+
*
|
|
4
|
+
* Instead of exposing 150+ tools to the LLM, exposes just 2:
|
|
5
|
+
* - discover_tools: Find tools matching a query
|
|
6
|
+
* - execute_tool: Execute a tool by path
|
|
7
|
+
*
|
|
8
|
+
* This reduces context window consumption from ~75,000 tokens to ~700 tokens.
|
|
9
|
+
*/
|
|
10
|
+
export { McpRouter, runStdioServer } from './server.js';
|
|
11
|
+
export type { RouterOptions } from './server.js';
|
|
12
|
+
export { loadConfigFromPaths, loadConfigFromFile, mergeConfigs } from './config/parser.js';
|
|
13
|
+
export { ToolSearchIndex } from './search/fuzzy.js';
|
|
14
|
+
export { McpClient, McpClientPool } from './executor/client.js';
|
|
15
|
+
export { DISCOVER_TOOLS_DEFINITION, handleDiscoverTools, estimateDiscoverToolsTokens, } from './tools/discover.js';
|
|
16
|
+
export { EXECUTE_TOOL_DEFINITION, handleExecuteTool, estimateExecuteToolTokens, } from './tools/execute.js';
|
|
17
|
+
export type { McpConfig, McpServerConfig, ToolDefinition, ToolSearchResult, DiscoverToolsArgs, ExecuteToolArgs, ToolRegistry, RouterStats, } from './types.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp-router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,SAAS,EACT,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Router - Lightweight Hierarchical Router for 98%+ Token Reduction
|
|
3
|
+
*
|
|
4
|
+
* Instead of exposing 150+ tools to the LLM, exposes just 2:
|
|
5
|
+
* - discover_tools: Find tools matching a query
|
|
6
|
+
* - execute_tool: Execute a tool by path
|
|
7
|
+
*
|
|
8
|
+
* This reduces context window consumption from ~75,000 tokens to ~700 tokens.
|
|
9
|
+
*/
|
|
10
|
+
export { McpRouter, runStdioServer } from './server.js';
|
|
11
|
+
export { loadConfigFromPaths, loadConfigFromFile, mergeConfigs } from './config/parser.js';
|
|
12
|
+
export { ToolSearchIndex } from './search/fuzzy.js';
|
|
13
|
+
export { McpClient, McpClientPool } from './executor/client.js';
|
|
14
|
+
export { DISCOVER_TOOLS_DEFINITION, handleDiscoverTools, estimateDiscoverToolsTokens, } from './tools/discover.js';
|
|
15
|
+
export { EXECUTE_TOOL_DEFINITION, handleExecuteTool, estimateExecuteToolTokens, } from './tools/execute.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp-router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy Search for MCP Tools
|
|
3
|
+
* Uses Fuse.js for semantic-like matching
|
|
4
|
+
*/
|
|
5
|
+
import type { ToolDefinition, ToolSearchResult } from '../types.js';
|
|
6
|
+
interface FuzzyOptions {
|
|
7
|
+
threshold?: number;
|
|
8
|
+
keys?: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare class ToolSearchIndex {
|
|
11
|
+
private tools;
|
|
12
|
+
private options;
|
|
13
|
+
constructor(options?: FuzzyOptions);
|
|
14
|
+
addTools(tools: ToolDefinition[]): void;
|
|
15
|
+
clear(): void;
|
|
16
|
+
search(query: string, limit?: number): ToolSearchResult[];
|
|
17
|
+
searchByServer(serverName: string, limit?: number): ToolSearchResult[];
|
|
18
|
+
getAllTools(): ToolDefinition[];
|
|
19
|
+
getToolByPath(path: string): ToolDefinition | undefined;
|
|
20
|
+
getStats(): {
|
|
21
|
+
servers: number;
|
|
22
|
+
tools: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=fuzzy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuzzy.d.ts","sourceRoot":"","sources":["../../../src/mcp-router/search/fuzzy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAKpE,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AA+BD,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,OAAO,CAAe;gBAElB,OAAO,GAAE,YAAiB;IAOtC,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,IAAI;IAIvC,KAAK,IAAI,IAAI;IAIb,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,gBAAgB,EAAE;IA4BrD,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,gBAAgB,EAAE;IAalE,WAAW,IAAI,cAAc,EAAE;IAI/B,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAKvD,QAAQ,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;CAO/C"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy Search for MCP Tools
|
|
3
|
+
* Uses Fuse.js for semantic-like matching
|
|
4
|
+
*/
|
|
5
|
+
function tokenize(str) {
|
|
6
|
+
return str.toLowerCase().split(/[\s_\-./]+/).filter(Boolean);
|
|
7
|
+
}
|
|
8
|
+
function calculateScore(query, target) {
|
|
9
|
+
const queryTokens = tokenize(query);
|
|
10
|
+
const targetTokens = tokenize(target);
|
|
11
|
+
if (queryTokens.length === 0 || targetTokens.length === 0)
|
|
12
|
+
return 0;
|
|
13
|
+
let matchCount = 0;
|
|
14
|
+
let partialMatches = 0;
|
|
15
|
+
for (const qToken of queryTokens) {
|
|
16
|
+
for (const tToken of targetTokens) {
|
|
17
|
+
if (tToken === qToken) {
|
|
18
|
+
matchCount++;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
else if (tToken.includes(qToken) || qToken.includes(tToken)) {
|
|
22
|
+
partialMatches += 0.5;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const totalMatches = matchCount + partialMatches;
|
|
28
|
+
return totalMatches / queryTokens.length;
|
|
29
|
+
}
|
|
30
|
+
export class ToolSearchIndex {
|
|
31
|
+
tools = [];
|
|
32
|
+
options;
|
|
33
|
+
constructor(options = {}) {
|
|
34
|
+
this.options = {
|
|
35
|
+
threshold: options.threshold ?? 0.3,
|
|
36
|
+
keys: options.keys ?? ['name', 'description'],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
addTools(tools) {
|
|
40
|
+
this.tools.push(...tools);
|
|
41
|
+
}
|
|
42
|
+
clear() {
|
|
43
|
+
this.tools = [];
|
|
44
|
+
}
|
|
45
|
+
search(query, limit = 10) {
|
|
46
|
+
const results = [];
|
|
47
|
+
for (const tool of this.tools) {
|
|
48
|
+
// Calculate score across all searchable fields
|
|
49
|
+
const nameScore = calculateScore(query, tool.name) * 1.5; // Weight name higher
|
|
50
|
+
const descScore = calculateScore(query, tool.description);
|
|
51
|
+
const serverScore = calculateScore(query, tool.serverName) * 0.5;
|
|
52
|
+
const score = Math.max(nameScore, descScore, serverScore);
|
|
53
|
+
if (score >= this.options.threshold) {
|
|
54
|
+
results.push({ tool, score });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Sort by score descending
|
|
58
|
+
results.sort((a, b) => b.score - a.score);
|
|
59
|
+
return results.slice(0, limit).map(({ tool, score }) => ({
|
|
60
|
+
path: `${tool.serverName}.${tool.name}`,
|
|
61
|
+
name: tool.name,
|
|
62
|
+
description: tool.description,
|
|
63
|
+
server: tool.serverName,
|
|
64
|
+
score: Math.round(score * 100) / 100,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
searchByServer(serverName, limit = 50) {
|
|
68
|
+
return this.tools
|
|
69
|
+
.filter(t => t.serverName === serverName)
|
|
70
|
+
.slice(0, limit)
|
|
71
|
+
.map(tool => ({
|
|
72
|
+
path: `${tool.serverName}.${tool.name}`,
|
|
73
|
+
name: tool.name,
|
|
74
|
+
description: tool.description,
|
|
75
|
+
server: tool.serverName,
|
|
76
|
+
score: 1.0,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
getAllTools() {
|
|
80
|
+
return [...this.tools];
|
|
81
|
+
}
|
|
82
|
+
getToolByPath(path) {
|
|
83
|
+
const [serverName, toolName] = path.split('.');
|
|
84
|
+
return this.tools.find(t => t.serverName === serverName && t.name === toolName);
|
|
85
|
+
}
|
|
86
|
+
getStats() {
|
|
87
|
+
const servers = new Set(this.tools.map(t => t.serverName));
|
|
88
|
+
return {
|
|
89
|
+
servers: servers.size,
|
|
90
|
+
tools: this.tools.length,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=fuzzy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuzzy.js","sourceRoot":"","sources":["../../../src/mcp-router/search/fuzzy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Router Server
|
|
3
|
+
* Exposes 2 meta-tools: discover_tools and execute_tool
|
|
4
|
+
* Achieves 98%+ token reduction by hiding individual tool definitions
|
|
5
|
+
*/
|
|
6
|
+
import { DISCOVER_TOOLS_DEFINITION } from './tools/discover.js';
|
|
7
|
+
import { EXECUTE_TOOL_DEFINITION } from './tools/execute.js';
|
|
8
|
+
import type { McpConfig, RouterStats } from './types.js';
|
|
9
|
+
export interface RouterOptions {
|
|
10
|
+
configPath?: string;
|
|
11
|
+
autoDiscover?: boolean;
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class McpRouter {
|
|
15
|
+
private config;
|
|
16
|
+
private searchIndex;
|
|
17
|
+
private clientPool;
|
|
18
|
+
private options;
|
|
19
|
+
private toolsLoaded;
|
|
20
|
+
constructor(options?: RouterOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Load tools from all configured MCP servers
|
|
23
|
+
*/
|
|
24
|
+
loadTools(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Get the 2 meta-tool definitions (for MCP tools/list)
|
|
27
|
+
*/
|
|
28
|
+
getToolDefinitions(): Array<typeof DISCOVER_TOOLS_DEFINITION | typeof EXECUTE_TOOL_DEFINITION>;
|
|
29
|
+
/**
|
|
30
|
+
* Handle tool call
|
|
31
|
+
*/
|
|
32
|
+
handleToolCall(name: string, args: unknown): Promise<unknown>;
|
|
33
|
+
/**
|
|
34
|
+
* Get router statistics
|
|
35
|
+
*/
|
|
36
|
+
getStats(): RouterStats;
|
|
37
|
+
/**
|
|
38
|
+
* Shutdown - cleanup all connections
|
|
39
|
+
*/
|
|
40
|
+
shutdown(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Get loaded config
|
|
43
|
+
*/
|
|
44
|
+
getConfig(): McpConfig;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Run as stdio MCP server
|
|
48
|
+
*/
|
|
49
|
+
export declare function runStdioServer(options?: RouterOptions): Promise<void>;
|
|
50
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp-router/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EACL,yBAAyB,EAG1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,uBAAuB,EAGxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAkB,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,GAAE,aAAkB;IAkBvC;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAuChC;;OAEG;IACH,kBAAkB,IAAI,KAAK,CAAC,OAAO,yBAAyB,GAAG,OAAO,uBAAuB,CAAC;IAI9F;;OAEG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBnE;;OAEG;IACH,QAAQ,IAAI,WAAW;IAsBvB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B;;OAEG;IACH,SAAS,IAAI,SAAS;CAGvB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsH/E"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Router Server
|
|
3
|
+
* Exposes 2 meta-tools: discover_tools and execute_tool
|
|
4
|
+
* Achieves 98%+ token reduction by hiding individual tool definitions
|
|
5
|
+
*/
|
|
6
|
+
import { loadConfigFromPaths, loadConfigFromFile } from './config/parser.js';
|
|
7
|
+
import { ToolSearchIndex } from './search/fuzzy.js';
|
|
8
|
+
import { McpClient, McpClientPool } from './executor/client.js';
|
|
9
|
+
import { DISCOVER_TOOLS_DEFINITION, handleDiscoverTools, estimateDiscoverToolsTokens, } from './tools/discover.js';
|
|
10
|
+
import { EXECUTE_TOOL_DEFINITION, handleExecuteTool, estimateExecuteToolTokens, } from './tools/execute.js';
|
|
11
|
+
export class McpRouter {
|
|
12
|
+
config;
|
|
13
|
+
searchIndex;
|
|
14
|
+
clientPool;
|
|
15
|
+
options;
|
|
16
|
+
toolsLoaded = false;
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.options = {
|
|
19
|
+
autoDiscover: true,
|
|
20
|
+
verbose: false,
|
|
21
|
+
...options,
|
|
22
|
+
};
|
|
23
|
+
// Load config
|
|
24
|
+
if (options.configPath) {
|
|
25
|
+
this.config = loadConfigFromFile(options.configPath);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.config = loadConfigFromPaths();
|
|
29
|
+
}
|
|
30
|
+
this.searchIndex = new ToolSearchIndex({ threshold: 0.2 });
|
|
31
|
+
this.clientPool = new McpClientPool();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Load tools from all configured MCP servers
|
|
35
|
+
*/
|
|
36
|
+
async loadTools() {
|
|
37
|
+
if (this.toolsLoaded)
|
|
38
|
+
return;
|
|
39
|
+
const servers = Object.entries(this.config.mcpServers);
|
|
40
|
+
if (this.options.verbose) {
|
|
41
|
+
console.error(`[router] Loading tools from ${servers.length} servers...`);
|
|
42
|
+
}
|
|
43
|
+
const allTools = [];
|
|
44
|
+
for (const [serverName, serverConfig] of servers) {
|
|
45
|
+
try {
|
|
46
|
+
const client = new McpClient(serverName, serverConfig);
|
|
47
|
+
await client.connect();
|
|
48
|
+
const tools = await client.listTools();
|
|
49
|
+
allTools.push(...tools);
|
|
50
|
+
client.disconnect();
|
|
51
|
+
if (this.options.verbose) {
|
|
52
|
+
console.error(`[router] ${serverName}: ${tools.length} tools`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (this.options.verbose) {
|
|
57
|
+
console.error(`[router] ${serverName}: failed to load - ${error}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
this.searchIndex.clear();
|
|
62
|
+
this.searchIndex.addTools(allTools);
|
|
63
|
+
this.toolsLoaded = true;
|
|
64
|
+
if (this.options.verbose) {
|
|
65
|
+
const stats = this.searchIndex.getStats();
|
|
66
|
+
console.error(`[router] Loaded ${stats.tools} tools from ${stats.servers} servers`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the 2 meta-tool definitions (for MCP tools/list)
|
|
71
|
+
*/
|
|
72
|
+
getToolDefinitions() {
|
|
73
|
+
return [DISCOVER_TOOLS_DEFINITION, EXECUTE_TOOL_DEFINITION];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Handle tool call
|
|
77
|
+
*/
|
|
78
|
+
async handleToolCall(name, args) {
|
|
79
|
+
// Ensure tools are loaded
|
|
80
|
+
if (!this.toolsLoaded && this.options.autoDiscover) {
|
|
81
|
+
await this.loadTools();
|
|
82
|
+
}
|
|
83
|
+
switch (name) {
|
|
84
|
+
case 'discover_tools':
|
|
85
|
+
return handleDiscoverTools(args, this.searchIndex);
|
|
86
|
+
case 'execute_tool':
|
|
87
|
+
return handleExecuteTool(args, this.searchIndex, this.clientPool);
|
|
88
|
+
default:
|
|
89
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get router statistics
|
|
94
|
+
*/
|
|
95
|
+
getStats() {
|
|
96
|
+
const { servers, tools } = this.searchIndex.getStats();
|
|
97
|
+
// Estimate traditional token usage: ~500 tokens per tool
|
|
98
|
+
const traditionalTokens = tools * 500;
|
|
99
|
+
// Router uses only 2 tools
|
|
100
|
+
const routerTokens = estimateDiscoverToolsTokens() + estimateExecuteToolTokens();
|
|
101
|
+
const savings = traditionalTokens > 0
|
|
102
|
+
? ((traditionalTokens - routerTokens) / traditionalTokens * 100).toFixed(1) + '%'
|
|
103
|
+
: '0%';
|
|
104
|
+
return {
|
|
105
|
+
totalServers: servers,
|
|
106
|
+
totalTools: tools,
|
|
107
|
+
traditionalTokens,
|
|
108
|
+
routerTokens,
|
|
109
|
+
savings,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Shutdown - cleanup all connections
|
|
114
|
+
*/
|
|
115
|
+
async shutdown() {
|
|
116
|
+
await this.clientPool.disconnectAll();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get loaded config
|
|
120
|
+
*/
|
|
121
|
+
getConfig() {
|
|
122
|
+
return this.config;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Run as stdio MCP server
|
|
127
|
+
*/
|
|
128
|
+
export async function runStdioServer(options = {}) {
|
|
129
|
+
const router = new McpRouter({ ...options, verbose: true });
|
|
130
|
+
let buffer = '';
|
|
131
|
+
function send(message) {
|
|
132
|
+
process.stdout.write(JSON.stringify(message) + '\n');
|
|
133
|
+
}
|
|
134
|
+
function handleMessage(message) {
|
|
135
|
+
const { id, method, params } = message;
|
|
136
|
+
switch (method) {
|
|
137
|
+
case 'initialize':
|
|
138
|
+
send({
|
|
139
|
+
jsonrpc: '2.0',
|
|
140
|
+
id,
|
|
141
|
+
result: {
|
|
142
|
+
protocolVersion: '2024-11-05',
|
|
143
|
+
capabilities: {
|
|
144
|
+
tools: {},
|
|
145
|
+
},
|
|
146
|
+
serverInfo: {
|
|
147
|
+
name: 'uam-mcp-router',
|
|
148
|
+
version: '1.0.0',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
break;
|
|
153
|
+
case 'notifications/initialized':
|
|
154
|
+
// No response needed
|
|
155
|
+
break;
|
|
156
|
+
case 'tools/list':
|
|
157
|
+
send({
|
|
158
|
+
jsonrpc: '2.0',
|
|
159
|
+
id,
|
|
160
|
+
result: {
|
|
161
|
+
tools: router.getToolDefinitions(),
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
break;
|
|
165
|
+
case 'tools/call': {
|
|
166
|
+
const { name, arguments: args } = params;
|
|
167
|
+
router.handleToolCall(name, args)
|
|
168
|
+
.then(result => {
|
|
169
|
+
send({
|
|
170
|
+
jsonrpc: '2.0',
|
|
171
|
+
id,
|
|
172
|
+
result: {
|
|
173
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
})
|
|
177
|
+
.catch(error => {
|
|
178
|
+
send({
|
|
179
|
+
jsonrpc: '2.0',
|
|
180
|
+
id,
|
|
181
|
+
error: {
|
|
182
|
+
code: -32000,
|
|
183
|
+
message: error instanceof Error ? error.message : String(error),
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
default:
|
|
190
|
+
if (id !== undefined) {
|
|
191
|
+
send({
|
|
192
|
+
jsonrpc: '2.0',
|
|
193
|
+
id,
|
|
194
|
+
error: {
|
|
195
|
+
code: -32601,
|
|
196
|
+
message: `Method not found: ${method}`,
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
process.stdin.on('data', (data) => {
|
|
203
|
+
buffer += data.toString();
|
|
204
|
+
const lines = buffer.split('\n');
|
|
205
|
+
buffer = lines.pop() || '';
|
|
206
|
+
for (const line of lines) {
|
|
207
|
+
if (!line.trim())
|
|
208
|
+
continue;
|
|
209
|
+
try {
|
|
210
|
+
const message = JSON.parse(line);
|
|
211
|
+
handleMessage(message);
|
|
212
|
+
}
|
|
213
|
+
catch (e) {
|
|
214
|
+
console.error('[router] Invalid JSON:', e);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
process.stdin.on('end', async () => {
|
|
219
|
+
await router.shutdown();
|
|
220
|
+
process.exit(0);
|
|
221
|
+
});
|
|
222
|
+
process.on('SIGINT', async () => {
|
|
223
|
+
await router.shutdown();
|
|
224
|
+
process.exit(0);
|
|
225
|
+
});
|
|
226
|
+
console.error('[router] MCP Router server started (stdio)');
|
|
227
|
+
console.error('[router] Exposing 2 tools: discover_tools, execute_tool');
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=server.js.map
|