@aiwerk/mcp-bridge 1.1.0 → 1.1.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/dist/src/config.js +9 -5
- package/dist/src/index.d.ts +1 -1
- package/dist/src/mcp-router.d.ts +5 -5
- package/dist/src/protocol.js +1 -1
- package/dist/src/smart-filter.js +6 -4
- package/dist/src/standalone-server.js +11 -3
- package/dist/src/transport-base.d.ts +5 -5
- package/dist/src/transport-sse.js +11 -19
- package/dist/src/transport-streamable-http.js +8 -6
- package/dist/src/types.d.ts +5 -0
- package/dist/src/update-checker.js +2 -2
- package/package.json +1 -1
package/dist/src/config.js
CHANGED
|
@@ -53,11 +53,9 @@ function resolveConfigValue(value, extraEnv) {
|
|
|
53
53
|
* 4. Validate required fields
|
|
54
54
|
*/
|
|
55
55
|
export function loadConfig(options = {}) {
|
|
56
|
-
const configDir = options.configPath
|
|
57
|
-
? join(options.configPath, "..") // If a file path is given, derive directory
|
|
58
|
-
: DEFAULT_CONFIG_DIR;
|
|
56
|
+
const configDir = getConfigDir(options.configPath);
|
|
59
57
|
const configPath = options.configPath || join(DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE);
|
|
60
|
-
const envPath = join(
|
|
58
|
+
const envPath = join(configDir, DEFAULT_ENV_FILE);
|
|
61
59
|
if (!existsSync(configPath)) {
|
|
62
60
|
throw new Error(`Config file not found: ${configPath}\nRun 'mcp-bridge init' to set up.`);
|
|
63
61
|
}
|
|
@@ -100,7 +98,13 @@ export function loadConfig(options = {}) {
|
|
|
100
98
|
}
|
|
101
99
|
/** Get the default config directory path. */
|
|
102
100
|
export function getConfigDir(configPath) {
|
|
103
|
-
|
|
101
|
+
if (!configPath)
|
|
102
|
+
return DEFAULT_CONFIG_DIR;
|
|
103
|
+
// If path ends with separator or has no extension, treat as directory
|
|
104
|
+
if (configPath.endsWith("/") || configPath.endsWith("\\") || !configPath.includes(".")) {
|
|
105
|
+
return configPath;
|
|
106
|
+
}
|
|
107
|
+
return join(configPath, "..");
|
|
104
108
|
}
|
|
105
109
|
/** Initialize the config directory with template files. */
|
|
106
110
|
export function initConfigDir(logger) {
|
package/dist/src/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type { RouterToolHint, RouterServerStatus, RouterDispatchResponse, Router
|
|
|
7
7
|
export { convertJsonSchemaToTypeBox, createToolParameters, setTypeBoxLoader, setSchemaLogger } from "./schema-convert.js";
|
|
8
8
|
export { initializeProtocol, fetchToolsList, PACKAGE_VERSION } from "./protocol.js";
|
|
9
9
|
export { loadConfig, parseEnvFile, initConfigDir, getConfigDir } from "./config.js";
|
|
10
|
-
export type { Logger, McpServerConfig, McpClientConfig, McpTool, McpRequest, McpResponse, McpTransport, McpServerConnection, BridgeConfig, } from "./types.js";
|
|
10
|
+
export type { Logger, McpServerConfig, McpClientConfig, McpTool, McpRequest, McpCallRequest, McpResponse, McpTransport, McpServerConnection, BridgeConfig, } from "./types.js";
|
|
11
11
|
export { nextRequestId } from "./types.js";
|
|
12
12
|
export { pickRegisteredToolName } from "./tool-naming.js";
|
|
13
13
|
export { StandaloneServer } from "./standalone-server.js";
|
package/dist/src/mcp-router.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpClientConfig, McpServerConfig, McpTransport } from "./types.js";
|
|
1
|
+
import { McpClientConfig, McpServerConfig, McpTransport, Logger } from "./types.js";
|
|
2
2
|
type RouterErrorCode = "unknown_server" | "unknown_tool" | "connection_failed" | "mcp_error" | "invalid_params";
|
|
3
3
|
export interface RouterToolHint {
|
|
4
4
|
name: string;
|
|
@@ -36,9 +36,9 @@ export type RouterDispatchResponse = {
|
|
|
36
36
|
code?: number;
|
|
37
37
|
};
|
|
38
38
|
export interface RouterTransportRefs {
|
|
39
|
-
sse: new (config: McpServerConfig, clientConfig: McpClientConfig, logger:
|
|
40
|
-
stdio: new (config: McpServerConfig, clientConfig: McpClientConfig, logger:
|
|
41
|
-
streamableHttp: new (config: McpServerConfig, clientConfig: McpClientConfig, logger:
|
|
39
|
+
sse: new (config: McpServerConfig, clientConfig: McpClientConfig, logger: Logger, onReconnected?: () => Promise<void>) => McpTransport;
|
|
40
|
+
stdio: new (config: McpServerConfig, clientConfig: McpClientConfig, logger: Logger, onReconnected?: () => Promise<void>) => McpTransport;
|
|
41
|
+
streamableHttp: new (config: McpServerConfig, clientConfig: McpClientConfig, logger: Logger, onReconnected?: () => Promise<void>) => McpTransport;
|
|
42
42
|
}
|
|
43
43
|
export declare class McpRouter {
|
|
44
44
|
private readonly servers;
|
|
@@ -48,7 +48,7 @@ export declare class McpRouter {
|
|
|
48
48
|
private readonly idleTimeoutMs;
|
|
49
49
|
private readonly maxConcurrent;
|
|
50
50
|
private readonly states;
|
|
51
|
-
constructor(servers: Record<string, McpServerConfig>, clientConfig: McpClientConfig, logger:
|
|
51
|
+
constructor(servers: Record<string, McpServerConfig>, clientConfig: McpClientConfig, logger: Logger, transportRefs?: Partial<RouterTransportRefs>);
|
|
52
52
|
static generateDescription(servers: Record<string, McpServerConfig>): string;
|
|
53
53
|
dispatch(server?: string, action?: string, tool?: string, params?: any): Promise<RouterDispatchResponse>;
|
|
54
54
|
getToolList(server: string): Promise<RouterToolHint[]>;
|
package/dist/src/protocol.js
CHANGED
|
@@ -5,7 +5,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
5
5
|
const __dirname = dirname(__filename);
|
|
6
6
|
export const PACKAGE_VERSION = (() => {
|
|
7
7
|
try {
|
|
8
|
-
return JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8")).version;
|
|
8
|
+
return JSON.parse(readFileSync(join(__dirname, "..", "..", "package.json"), "utf-8")).version;
|
|
9
9
|
}
|
|
10
10
|
catch {
|
|
11
11
|
return "0.0.0";
|
package/dist/src/smart-filter.js
CHANGED
|
@@ -178,7 +178,9 @@ export class SmartFilter {
|
|
|
178
178
|
const allServerWords = [...descriptionWords, ...keywordWords];
|
|
179
179
|
// Calculate overlaps
|
|
180
180
|
const descMatches = this.countOverlap(queryWords, descriptionWords);
|
|
181
|
-
|
|
181
|
+
// Count keyword matches that are NOT already counted in description
|
|
182
|
+
const keywordOnlyWords = keywordWords.filter(kw => !descriptionWords.includes(kw));
|
|
183
|
+
const keywordOnlyMatches = this.countOverlap(queryWords, keywordOnlyWords);
|
|
182
184
|
// Add basic synonym matching for common terms
|
|
183
185
|
let semanticMatches = 0;
|
|
184
186
|
for (const queryWord of queryWords) {
|
|
@@ -194,7 +196,7 @@ export class SmartFilter {
|
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
198
|
// Weighted scoring: description 1.0x, keywords 0.7x, semantic 0.5x, partial matches 0.3x
|
|
197
|
-
const score = (descMatches * 1.0 +
|
|
199
|
+
const score = (descMatches * 1.0 + keywordOnlyMatches * 0.7 + semanticMatches * 0.5 + partialMatches) / queryWords.length;
|
|
198
200
|
return score;
|
|
199
201
|
}
|
|
200
202
|
getSemanticScore(queryWord, serverWords) {
|
|
@@ -391,8 +393,8 @@ export const DEFAULTS = {
|
|
|
391
393
|
topServers: 5,
|
|
392
394
|
hardCap: 8,
|
|
393
395
|
topTools: 10,
|
|
394
|
-
serverThreshold: 0.
|
|
395
|
-
toolThreshold: 0.
|
|
396
|
+
serverThreshold: 0.01,
|
|
397
|
+
toolThreshold: 0.05,
|
|
396
398
|
fallback: "keyword",
|
|
397
399
|
alwaysInclude: [],
|
|
398
400
|
timeoutMs: 500,
|
|
@@ -246,9 +246,16 @@ export class StandaloneServer {
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
/** Connect to all backend servers and discover their tools (direct mode). */
|
|
249
|
-
async discoverDirectTools() {
|
|
250
|
-
if (this.directTools.length > 0)
|
|
249
|
+
async discoverDirectTools(force = false) {
|
|
250
|
+
if (this.directTools.length > 0 && !force)
|
|
251
251
|
return; // Already discovered
|
|
252
|
+
if (force) {
|
|
253
|
+
this.directTools = [];
|
|
254
|
+
for (const [, conn] of this.directConnections) {
|
|
255
|
+
await conn.transport.disconnect().catch(() => { });
|
|
256
|
+
}
|
|
257
|
+
this.directConnections.clear();
|
|
258
|
+
}
|
|
252
259
|
const globalNames = new Set();
|
|
253
260
|
for (const [serverName, serverConfig] of Object.entries(this.config.servers)) {
|
|
254
261
|
try {
|
|
@@ -279,7 +286,8 @@ export class StandaloneServer {
|
|
|
279
286
|
}
|
|
280
287
|
createTransport(serverName, serverConfig) {
|
|
281
288
|
const onReconnected = async () => {
|
|
282
|
-
this.logger.info(`[mcp-bridge] ${serverName} reconnected`);
|
|
289
|
+
this.logger.info(`[mcp-bridge] ${serverName} reconnected, refreshing tools`);
|
|
290
|
+
await this.discoverDirectTools(true);
|
|
283
291
|
};
|
|
284
292
|
switch (serverConfig.transport) {
|
|
285
293
|
case "sse":
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpTransport, McpRequest, McpResponse, McpServerConfig } from "./types.js";
|
|
1
|
+
import { McpTransport, McpRequest, McpResponse, McpServerConfig, McpClientConfig, Logger } from "./types.js";
|
|
2
2
|
export type PendingRequest = {
|
|
3
3
|
resolve: Function;
|
|
4
4
|
reject: Function;
|
|
@@ -14,14 +14,14 @@ export type PendingRequest = {
|
|
|
14
14
|
*/
|
|
15
15
|
export declare abstract class BaseTransport implements McpTransport {
|
|
16
16
|
protected config: McpServerConfig;
|
|
17
|
-
protected clientConfig:
|
|
17
|
+
protected clientConfig: McpClientConfig;
|
|
18
18
|
protected connected: boolean;
|
|
19
19
|
protected pendingRequests: Map<number, PendingRequest>;
|
|
20
|
-
protected logger:
|
|
20
|
+
protected logger: Logger;
|
|
21
21
|
protected reconnectTimer: NodeJS.Timeout | null;
|
|
22
22
|
protected onReconnected?: () => Promise<void>;
|
|
23
23
|
protected backoffDelay: number;
|
|
24
|
-
constructor(config: McpServerConfig, clientConfig:
|
|
24
|
+
constructor(config: McpServerConfig, clientConfig: McpClientConfig, logger: Logger, onReconnected?: () => Promise<void>);
|
|
25
25
|
abstract connect(): Promise<void>;
|
|
26
26
|
abstract disconnect(): Promise<void>;
|
|
27
27
|
abstract sendRequest(request: McpRequest): Promise<McpResponse>;
|
|
@@ -73,4 +73,4 @@ export declare function resolveArgs(args: string[], extraEnv?: Record<string, st
|
|
|
73
73
|
/**
|
|
74
74
|
* Warn if a URL uses non-TLS HTTP to a remote (non-localhost) host.
|
|
75
75
|
*/
|
|
76
|
-
export declare function warnIfNonTlsRemoteUrl(rawUrl: string, logger:
|
|
76
|
+
export declare function warnIfNonTlsRemoteUrl(rawUrl: string, logger: Logger): void;
|
|
@@ -52,7 +52,7 @@ export class SseTransport extends BaseTransport {
|
|
|
52
52
|
const reader = response.body.getReader();
|
|
53
53
|
const decoder = new TextDecoder();
|
|
54
54
|
let buffer = "";
|
|
55
|
-
|
|
55
|
+
const state = { event: "", dataBuffer: [] };
|
|
56
56
|
while (true) {
|
|
57
57
|
const { done, value } = await reader.read();
|
|
58
58
|
if (done)
|
|
@@ -61,17 +61,7 @@ export class SseTransport extends BaseTransport {
|
|
|
61
61
|
const lines = buffer.split('\n');
|
|
62
62
|
buffer = lines.pop() || "";
|
|
63
63
|
for (const line of lines) {
|
|
64
|
-
|
|
65
|
-
if (trimmed.startsWith("event: ")) {
|
|
66
|
-
currentEvent = trimmed.substring(7).trim();
|
|
67
|
-
}
|
|
68
|
-
else if (trimmed === "") {
|
|
69
|
-
this.processEventLine(line, currentEvent);
|
|
70
|
-
currentEvent = "";
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
this.processEventLine(line, currentEvent);
|
|
74
|
-
}
|
|
64
|
+
this.processEventLine(line, state);
|
|
75
65
|
}
|
|
76
66
|
}
|
|
77
67
|
}
|
|
@@ -82,20 +72,22 @@ export class SseTransport extends BaseTransport {
|
|
|
82
72
|
this.scheduleReconnect();
|
|
83
73
|
}
|
|
84
74
|
}
|
|
85
|
-
processEventLine(line,
|
|
75
|
+
processEventLine(line, state) {
|
|
86
76
|
const trimmed = line.trim();
|
|
87
|
-
if (trimmed.startsWith("event: "))
|
|
77
|
+
if (trimmed.startsWith("event: ")) {
|
|
78
|
+
state.event = trimmed.substring(7).trim();
|
|
88
79
|
return;
|
|
80
|
+
}
|
|
89
81
|
if (trimmed.startsWith("data: ")) {
|
|
90
|
-
|
|
82
|
+
state.dataBuffer.push(trimmed.substring(6));
|
|
91
83
|
return;
|
|
92
84
|
}
|
|
93
85
|
if (trimmed === "") {
|
|
94
|
-
if (
|
|
86
|
+
if (state.dataBuffer.length === 0)
|
|
95
87
|
return;
|
|
96
|
-
const data =
|
|
97
|
-
|
|
98
|
-
if (
|
|
88
|
+
const data = state.dataBuffer.join("\n");
|
|
89
|
+
state.dataBuffer.length = 0;
|
|
90
|
+
if (state.event === "endpoint") {
|
|
99
91
|
if (data.startsWith("/")) {
|
|
100
92
|
const base = new URL(this.config.url);
|
|
101
93
|
this.endpointUrl = `${base.origin}${data}`;
|
|
@@ -60,17 +60,19 @@ export class StreamableHttpTransport extends BaseTransport {
|
|
|
60
60
|
const dataLines = text.split('\n')
|
|
61
61
|
.filter((line) => line.startsWith('data:'))
|
|
62
62
|
.map((line) => line.substring(5).trim());
|
|
63
|
-
if (dataLines.length
|
|
64
|
-
jsonResponse = JSON.parse(dataLines[dataLines.length - 1]);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
63
|
+
if (dataLines.length === 0) {
|
|
67
64
|
throw new Error("No data lines in SSE response");
|
|
68
65
|
}
|
|
66
|
+
for (const dl of dataLines) {
|
|
67
|
+
try {
|
|
68
|
+
this.handleMessage(JSON.parse(dl));
|
|
69
|
+
}
|
|
70
|
+
catch { /* skip malformed lines */ }
|
|
71
|
+
}
|
|
69
72
|
}
|
|
70
73
|
else {
|
|
71
|
-
|
|
74
|
+
this.handleMessage(await response.json());
|
|
72
75
|
}
|
|
73
|
-
this.handleMessage(jsonResponse);
|
|
74
76
|
}
|
|
75
77
|
catch (error) {
|
|
76
78
|
clearTimeout(timeout);
|
package/dist/src/types.d.ts
CHANGED
|
@@ -30,12 +30,17 @@ export interface McpTool {
|
|
|
30
30
|
description: string;
|
|
31
31
|
inputSchema: any;
|
|
32
32
|
}
|
|
33
|
+
/** MCP JSON-RPC request. id is required for requests (omit only for notifications). */
|
|
33
34
|
export interface McpRequest {
|
|
34
35
|
jsonrpc: "2.0";
|
|
35
36
|
id?: number;
|
|
36
37
|
method: string;
|
|
37
38
|
params?: any;
|
|
38
39
|
}
|
|
40
|
+
/** MCP request that requires a response (id is mandatory). */
|
|
41
|
+
export interface McpCallRequest extends McpRequest {
|
|
42
|
+
id: number;
|
|
43
|
+
}
|
|
39
44
|
export declare function nextRequestId(): number;
|
|
40
45
|
export interface McpResponse {
|
|
41
46
|
jsonrpc: "2.0";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execSync, exec as execCb } from "child_process";
|
|
1
|
+
import { execSync, exec as execCb, execFile } from "child_process";
|
|
2
2
|
import { PACKAGE_VERSION } from "./protocol.js";
|
|
3
3
|
const PACKAGE_NAME = "@aiwerk/mcp-bridge";
|
|
4
4
|
let cachedUpdateInfo = null;
|
|
@@ -87,7 +87,7 @@ export async function runUpdate(logger) {
|
|
|
87
87
|
function npmViewVersion(_logger) {
|
|
88
88
|
return new Promise((resolve, reject) => {
|
|
89
89
|
const timeout = setTimeout(() => reject(new Error("npm view timed out")), 10_000);
|
|
90
|
-
|
|
90
|
+
execFile("npm", ["view", PACKAGE_NAME, "version"], { encoding: "utf-8" }, (err, stdout) => {
|
|
91
91
|
clearTimeout(timeout);
|
|
92
92
|
if (err)
|
|
93
93
|
return reject(err);
|