@mcp-rating/gateway 0.2.0
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/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/ads/ad-tracker.d.ts +63 -0
- package/dist/ads/ad-tracker.js +144 -0
- package/dist/ads/ad-tracker.js.map +1 -0
- package/dist/analytics/usage-tracker.d.ts +67 -0
- package/dist/analytics/usage-tracker.js +148 -0
- package/dist/analytics/usage-tracker.js.map +1 -0
- package/dist/audit/audit-log.d.ts +71 -0
- package/dist/audit/audit-log.js +72 -0
- package/dist/audit/audit-log.js.map +1 -0
- package/dist/config/gateway-config.d.ts +6 -0
- package/dist/config/gateway-config.js +135 -0
- package/dist/config/gateway-config.js.map +1 -0
- package/dist/config/groups.d.ts +29 -0
- package/dist/config/groups.js +102 -0
- package/dist/config/groups.js.map +1 -0
- package/dist/config/profiles.d.ts +42 -0
- package/dist/config/profiles.js +167 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/types.d.ts +76 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/connection/auto-installer.d.ts +13 -0
- package/dist/connection/auto-installer.js +61 -0
- package/dist/connection/auto-installer.js.map +1 -0
- package/dist/connection/connection-manager.d.ts +106 -0
- package/dist/connection/connection-manager.js +845 -0
- package/dist/connection/connection-manager.js.map +1 -0
- package/dist/connection/downstream-client.d.ts +50 -0
- package/dist/connection/downstream-client.js +170 -0
- package/dist/connection/downstream-client.js.map +1 -0
- package/dist/connection/http-client.d.ts +12 -0
- package/dist/connection/http-client.js +26 -0
- package/dist/connection/http-client.js.map +1 -0
- package/dist/connection/sse-client.d.ts +12 -0
- package/dist/connection/sse-client.js +26 -0
- package/dist/connection/sse-client.js.map +1 -0
- package/dist/connection/transport-factory.d.ts +31 -0
- package/dist/connection/transport-factory.js +67 -0
- package/dist/connection/transport-factory.js.map +1 -0
- package/dist/connection/types.d.ts +134 -0
- package/dist/connection/types.js +5 -0
- package/dist/connection/types.js.map +1 -0
- package/dist/connection/ws-client.d.ts +12 -0
- package/dist/connection/ws-client.js +26 -0
- package/dist/connection/ws-client.js.map +1 -0
- package/dist/discovery/auto-recommender.d.ts +51 -0
- package/dist/discovery/auto-recommender.js +176 -0
- package/dist/discovery/auto-recommender.js.map +1 -0
- package/dist/gateway-server.d.ts +42 -0
- package/dist/gateway-server.js +135 -0
- package/dist/gateway-server.js.map +1 -0
- package/dist/http-daemon.d.ts +26 -0
- package/dist/http-daemon.js +302 -0
- package/dist/http-daemon.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/meta-tools/ad-status.d.ts +8 -0
- package/dist/meta-tools/ad-status.js +41 -0
- package/dist/meta-tools/ad-status.js.map +1 -0
- package/dist/meta-tools/audit.d.ts +12 -0
- package/dist/meta-tools/audit.js +98 -0
- package/dist/meta-tools/audit.js.map +1 -0
- package/dist/meta-tools/call-tool.d.ts +11 -0
- package/dist/meta-tools/call-tool.js +65 -0
- package/dist/meta-tools/call-tool.js.map +1 -0
- package/dist/meta-tools/connect.d.ts +3 -0
- package/dist/meta-tools/connect.js +105 -0
- package/dist/meta-tools/connect.js.map +1 -0
- package/dist/meta-tools/disconnect.d.ts +3 -0
- package/dist/meta-tools/disconnect.js +30 -0
- package/dist/meta-tools/disconnect.js.map +1 -0
- package/dist/meta-tools/discover.d.ts +3 -0
- package/dist/meta-tools/discover.js +97 -0
- package/dist/meta-tools/discover.js.map +1 -0
- package/dist/meta-tools/groups.d.ts +7 -0
- package/dist/meta-tools/groups.js +237 -0
- package/dist/meta-tools/groups.js.map +1 -0
- package/dist/meta-tools/health.d.ts +4 -0
- package/dist/meta-tools/health.js +76 -0
- package/dist/meta-tools/health.js.map +1 -0
- package/dist/meta-tools/index.d.ts +28 -0
- package/dist/meta-tools/index.js +56 -0
- package/dist/meta-tools/index.js.map +1 -0
- package/dist/meta-tools/list-active.d.ts +3 -0
- package/dist/meta-tools/list-active.js +50 -0
- package/dist/meta-tools/list-active.js.map +1 -0
- package/dist/meta-tools/profiles.d.ts +6 -0
- package/dist/meta-tools/profiles.js +197 -0
- package/dist/meta-tools/profiles.js.map +1 -0
- package/dist/meta-tools/recommend.d.ts +14 -0
- package/dist/meta-tools/recommend.js +74 -0
- package/dist/meta-tools/recommend.js.map +1 -0
- package/dist/meta-tools/sandbox.d.ts +12 -0
- package/dist/meta-tools/sandbox.js +125 -0
- package/dist/meta-tools/sandbox.js.map +1 -0
- package/dist/meta-tools/server-info.d.ts +4 -0
- package/dist/meta-tools/server-info.js +156 -0
- package/dist/meta-tools/server-info.js.map +1 -0
- package/dist/meta-tools/usage.d.ts +6 -0
- package/dist/meta-tools/usage.js +149 -0
- package/dist/meta-tools/usage.js.map +1 -0
- package/dist/permissions/permission-manager.d.ts +26 -0
- package/dist/permissions/permission-manager.js +73 -0
- package/dist/permissions/permission-manager.js.map +1 -0
- package/dist/permissions/trust-tiers.d.ts +18 -0
- package/dist/permissions/trust-tiers.js +42 -0
- package/dist/permissions/trust-tiers.js.map +1 -0
- package/dist/plugins/builtin/logging-plugin.d.ts +6 -0
- package/dist/plugins/builtin/logging-plugin.js +41 -0
- package/dist/plugins/builtin/logging-plugin.js.map +1 -0
- package/dist/plugins/plugin-manager.d.ts +65 -0
- package/dist/plugins/plugin-manager.js +137 -0
- package/dist/plugins/plugin-manager.js.map +1 -0
- package/dist/proxy/prime-handlers.d.ts +27 -0
- package/dist/proxy/prime-handlers.js +47 -0
- package/dist/proxy/prime-handlers.js.map +1 -0
- package/dist/proxy/progress-forwarder.d.ts +44 -0
- package/dist/proxy/progress-forwarder.js +40 -0
- package/dist/proxy/progress-forwarder.js.map +1 -0
- package/dist/proxy/prompt-router.d.ts +25 -0
- package/dist/proxy/prompt-router.js +110 -0
- package/dist/proxy/prompt-router.js.map +1 -0
- package/dist/proxy/resource-router.d.ts +25 -0
- package/dist/proxy/resource-router.js +91 -0
- package/dist/proxy/resource-router.js.map +1 -0
- package/dist/proxy/schema-adapter.d.ts +11 -0
- package/dist/proxy/schema-adapter.js +36 -0
- package/dist/proxy/schema-adapter.js.map +1 -0
- package/dist/proxy/task-proxy.d.ts +28 -0
- package/dist/proxy/task-proxy.js +186 -0
- package/dist/proxy/task-proxy.js.map +1 -0
- package/dist/proxy/tool-router.d.ts +38 -0
- package/dist/proxy/tool-router.js +229 -0
- package/dist/proxy/tool-router.js.map +1 -0
- package/dist/proxy/ui-detect.d.ts +22 -0
- package/dist/proxy/ui-detect.js +30 -0
- package/dist/proxy/ui-detect.js.map +1 -0
- package/dist/registry/registry-client.d.ts +55 -0
- package/dist/registry/registry-client.js +211 -0
- package/dist/registry/registry-client.js.map +1 -0
- package/dist/registry/types.d.ts +69 -0
- package/dist/registry/types.js +2 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/sandbox/container-runtime.d.ts +91 -0
- package/dist/sandbox/container-runtime.js +178 -0
- package/dist/sandbox/container-runtime.js.map +1 -0
- package/dist/sandbox/egress-proxy.d.ts +51 -0
- package/dist/sandbox/egress-proxy.js +146 -0
- package/dist/sandbox/egress-proxy.js.map +1 -0
- package/dist/sandbox/env-scoper.d.ts +29 -0
- package/dist/sandbox/env-scoper.js +60 -0
- package/dist/sandbox/env-scoper.js.map +1 -0
- package/dist/sandbox/manifest-resolver.d.ts +40 -0
- package/dist/sandbox/manifest-resolver.js +185 -0
- package/dist/sandbox/manifest-resolver.js.map +1 -0
- package/dist/sandbox/types.d.ts +88 -0
- package/dist/sandbox/types.js +21 -0
- package/dist/sandbox/types.js.map +1 -0
- package/dist/utils/errors.d.ts +39 -0
- package/dist/utils/errors.js +67 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.js +51 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
|
|
2
|
+
import { connectClientAndFetchCapabilities, timeoutReject } from "./downstream-client.js";
|
|
3
|
+
import { log } from "../utils/logger.js";
|
|
4
|
+
/** Default timeout for WebSocket connection (30s) */
|
|
5
|
+
const WS_CONNECTION_TIMEOUT_MS = 30_000;
|
|
6
|
+
/**
|
|
7
|
+
* Connect to a downstream MCP server via WebSocket transport.
|
|
8
|
+
*/
|
|
9
|
+
export async function connectViaWebSocket(params) {
|
|
10
|
+
const { slug, url, onClose, onToolsChanged, connectionTimeoutMs = WS_CONNECTION_TIMEOUT_MS, } = params;
|
|
11
|
+
log.info("Connecting via WebSocket", { slug, url });
|
|
12
|
+
const result = await Promise.race([
|
|
13
|
+
doWsConnect(slug, url, onClose, onToolsChanged),
|
|
14
|
+
timeoutReject(connectionTimeoutMs, `Timed out connecting to "${slug}" via WebSocket after ${connectionTimeoutMs}ms`),
|
|
15
|
+
]);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
async function doWsConnect(slug, url, onClose, onToolsChanged) {
|
|
19
|
+
const transport = new WebSocketClientTransport(new URL(url));
|
|
20
|
+
if (onClose) {
|
|
21
|
+
transport.onclose = onClose;
|
|
22
|
+
}
|
|
23
|
+
const { client, serverInfo, tools, resources, prompts } = await connectClientAndFetchCapabilities(slug, transport, onToolsChanged);
|
|
24
|
+
return { client, transport, tools, resources, prompts, serverInfo };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=ws-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../../src/connection/ws-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AAEzF,OAAO,EAAE,iCAAiC,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAEzC,qDAAqD;AACrD,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAUxC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAuB;IAEvB,MAAM,EACJ,IAAI,EACJ,GAAG,EACH,OAAO,EACP,cAAc,EACd,mBAAmB,GAAG,wBAAwB,GAC/C,GAAG,MAAM,CAAC;IAEX,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAEpD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;QAChC,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,cAAc,CAAC;QAC/C,aAAa,CACX,mBAAmB,EACnB,4BAA4B,IAAI,yBAAyB,mBAAmB,IAAI,CACjF;KACF,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,IAAY,EACZ,GAAW,EACX,OAAiC,EACjC,cAAwC;IAExC,MAAM,SAAS,GAAG,IAAI,wBAAwB,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAE7D,IAAI,OAAO,EAAE,CAAC;QACZ,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;IAC9B,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,GACrD,MAAM,iCAAiC,CAAC,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IAE3E,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { RegistryClient } from "../registry/registry-client.js";
|
|
2
|
+
import type { ConnectionManager } from "../connection/connection-manager.js";
|
|
3
|
+
import type { UsageTracker, ToolCallEvent } from "../analytics/usage-tracker.js";
|
|
4
|
+
export interface ServerRecommendation {
|
|
5
|
+
slug: string;
|
|
6
|
+
name: string;
|
|
7
|
+
reason: string;
|
|
8
|
+
qualityScore: number;
|
|
9
|
+
relevanceScore: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Analyzes tool call patterns and recommends relevant servers.
|
|
13
|
+
*/
|
|
14
|
+
export declare class AutoRecommender {
|
|
15
|
+
private readonly registryClient;
|
|
16
|
+
private readonly connectionManager;
|
|
17
|
+
constructor(registryClient: RegistryClient, connectionManager: ConnectionManager);
|
|
18
|
+
/**
|
|
19
|
+
* Intent-based recommendation: search the registry for servers matching
|
|
20
|
+
* a free-text goal/query, ranked by quality + relevance.
|
|
21
|
+
*
|
|
22
|
+
* This is the agent-friendly path — the agent passes the user's actual
|
|
23
|
+
* intent and gets back relevant servers regardless of past usage.
|
|
24
|
+
* Server-side intent expansion (cloud storage → dropbox/gdrive/...)
|
|
25
|
+
* runs transparently.
|
|
26
|
+
*/
|
|
27
|
+
recommendByQuery(query: string, options?: {
|
|
28
|
+
limit?: number;
|
|
29
|
+
category?: string;
|
|
30
|
+
}): Promise<ServerRecommendation[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Analyze recent tool calls and suggest relevant servers from the registry.
|
|
33
|
+
*/
|
|
34
|
+
recommend(recentCalls: ToolCallEvent[], usageTracker?: UsageTracker): Promise<ServerRecommendation[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Extract keywords from tool call patterns.
|
|
37
|
+
*/
|
|
38
|
+
private extractKeywords;
|
|
39
|
+
/**
|
|
40
|
+
* Compute a relevance score for a server against the extracted keywords.
|
|
41
|
+
*/
|
|
42
|
+
private computeRelevance;
|
|
43
|
+
/**
|
|
44
|
+
* Build a human-readable reason for the recommendation.
|
|
45
|
+
*/
|
|
46
|
+
private buildReason;
|
|
47
|
+
/**
|
|
48
|
+
* Build a human-readable reason for an intent-based (query) recommendation.
|
|
49
|
+
*/
|
|
50
|
+
private buildIntentReason;
|
|
51
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { log } from "../utils/logger.js";
|
|
2
|
+
// ── Auto-Recommender ─────────────────────────────────────────────────────────
|
|
3
|
+
/**
|
|
4
|
+
* Analyzes tool call patterns and recommends relevant servers.
|
|
5
|
+
*/
|
|
6
|
+
export class AutoRecommender {
|
|
7
|
+
registryClient;
|
|
8
|
+
connectionManager;
|
|
9
|
+
constructor(registryClient, connectionManager) {
|
|
10
|
+
this.registryClient = registryClient;
|
|
11
|
+
this.connectionManager = connectionManager;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Intent-based recommendation: search the registry for servers matching
|
|
15
|
+
* a free-text goal/query, ranked by quality + relevance.
|
|
16
|
+
*
|
|
17
|
+
* This is the agent-friendly path — the agent passes the user's actual
|
|
18
|
+
* intent and gets back relevant servers regardless of past usage.
|
|
19
|
+
* Server-side intent expansion (cloud storage → dropbox/gdrive/...)
|
|
20
|
+
* runs transparently.
|
|
21
|
+
*/
|
|
22
|
+
async recommendByQuery(query, options = {}) {
|
|
23
|
+
const limit = options.limit ?? 5;
|
|
24
|
+
const connectedSlugs = new Set(this.connectionManager.listConnections().map((c) => c.slug));
|
|
25
|
+
try {
|
|
26
|
+
// Over-fetch so we can filter out connected servers and still hit the cap
|
|
27
|
+
const result = await this.registryClient.search(query, {
|
|
28
|
+
limit: Math.max(limit * 3, 15),
|
|
29
|
+
category: options.category,
|
|
30
|
+
});
|
|
31
|
+
const recommendations = [];
|
|
32
|
+
const queryTerms = query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
33
|
+
for (const server of result.data) {
|
|
34
|
+
if (connectedSlugs.has(server.slug))
|
|
35
|
+
continue;
|
|
36
|
+
const relevance = this.computeRelevance(server, queryTerms);
|
|
37
|
+
recommendations.push({
|
|
38
|
+
slug: server.slug,
|
|
39
|
+
name: server.name || server.slug,
|
|
40
|
+
reason: this.buildIntentReason(server, query, queryTerms),
|
|
41
|
+
qualityScore: server.qualityScore ?? 0,
|
|
42
|
+
relevanceScore: relevance,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// Sort by combined quality + relevance. For intent queries, registry
|
|
46
|
+
// ranking already incorporates semantic + FTS + intent expansion, so
|
|
47
|
+
// we lean slightly more on quality to break ties.
|
|
48
|
+
return recommendations
|
|
49
|
+
.sort((a, b) => b.qualityScore * 0.5 + b.relevanceScore * 0.5 -
|
|
50
|
+
(a.qualityScore * 0.5 + a.relevanceScore * 0.5))
|
|
51
|
+
.slice(0, limit);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
log.warn("Registry search failed for intent query", {
|
|
55
|
+
query,
|
|
56
|
+
error: String(err),
|
|
57
|
+
});
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Analyze recent tool calls and suggest relevant servers from the registry.
|
|
63
|
+
*/
|
|
64
|
+
async recommend(recentCalls, usageTracker) {
|
|
65
|
+
// Extract keywords from recent tool usage patterns
|
|
66
|
+
const keywords = this.extractKeywords(recentCalls);
|
|
67
|
+
if (keywords.length === 0) {
|
|
68
|
+
log.debug("No keywords extracted from recent calls, using general recommendations");
|
|
69
|
+
keywords.push("popular", "tools");
|
|
70
|
+
}
|
|
71
|
+
log.debug("Recommending servers based on usage patterns", { keywords });
|
|
72
|
+
// Search registry for each keyword set
|
|
73
|
+
const allResults = new Map();
|
|
74
|
+
const connectedSlugs = new Set(this.connectionManager.listConnections().map((c) => c.slug));
|
|
75
|
+
for (const keyword of keywords.slice(0, 5)) {
|
|
76
|
+
try {
|
|
77
|
+
const results = await this.registryClient.search(keyword, { limit: 10 });
|
|
78
|
+
for (const server of results.data) {
|
|
79
|
+
// Skip already-connected servers
|
|
80
|
+
if (connectedSlugs.has(server.slug))
|
|
81
|
+
continue;
|
|
82
|
+
// Skip if already found with a better score
|
|
83
|
+
const existing = allResults.get(server.slug);
|
|
84
|
+
const relevance = this.computeRelevance(server, keywords);
|
|
85
|
+
if (existing && existing.relevanceScore >= relevance)
|
|
86
|
+
continue;
|
|
87
|
+
allResults.set(server.slug, {
|
|
88
|
+
slug: server.slug,
|
|
89
|
+
name: server.name || server.slug,
|
|
90
|
+
reason: this.buildReason(server, keywords),
|
|
91
|
+
qualityScore: server.qualityScore ?? 0,
|
|
92
|
+
relevanceScore: relevance,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
log.debug("Registry search failed for keyword", {
|
|
98
|
+
keyword,
|
|
99
|
+
error: String(err),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Sort by combined relevance + quality score
|
|
104
|
+
const recommendations = [...allResults.values()]
|
|
105
|
+
.sort((a, b) => b.relevanceScore * 0.6 + b.qualityScore * 0.4 -
|
|
106
|
+
(a.relevanceScore * 0.6 + a.qualityScore * 0.4))
|
|
107
|
+
.slice(0, 5);
|
|
108
|
+
return recommendations;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract keywords from tool call patterns.
|
|
112
|
+
*/
|
|
113
|
+
extractKeywords(calls) {
|
|
114
|
+
const words = new Map();
|
|
115
|
+
for (const call of calls) {
|
|
116
|
+
// Extract words from tool names
|
|
117
|
+
const parts = call.toolName
|
|
118
|
+
.replace(/[_-]/g, " ")
|
|
119
|
+
.split(/\s+/)
|
|
120
|
+
.filter((w) => w.length > 2);
|
|
121
|
+
for (const part of parts) {
|
|
122
|
+
const lower = part.toLowerCase();
|
|
123
|
+
words.set(lower, (words.get(lower) || 0) + 1);
|
|
124
|
+
}
|
|
125
|
+
// Extract category hints from slug
|
|
126
|
+
const slugParts = call.slug
|
|
127
|
+
.replace(/[_\-@/.]/g, " ")
|
|
128
|
+
.split(/\s+/)
|
|
129
|
+
.filter((w) => w.length > 2);
|
|
130
|
+
for (const part of slugParts) {
|
|
131
|
+
const lower = part.toLowerCase();
|
|
132
|
+
words.set(lower, (words.get(lower) || 0) + 0.5);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Sort by frequency, take top keywords
|
|
136
|
+
return [...words.entries()]
|
|
137
|
+
.sort((a, b) => b[1] - a[1])
|
|
138
|
+
.slice(0, 8)
|
|
139
|
+
.map(([word]) => word);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Compute a relevance score for a server against the extracted keywords.
|
|
143
|
+
*/
|
|
144
|
+
computeRelevance(server, keywords) {
|
|
145
|
+
let score = 0;
|
|
146
|
+
const text = `${server.name} ${server.description || ""} ${server.slug}`.toLowerCase();
|
|
147
|
+
for (const keyword of keywords) {
|
|
148
|
+
if (text.includes(keyword)) {
|
|
149
|
+
score += 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return score / Math.max(keywords.length, 1);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Build a human-readable reason for the recommendation.
|
|
156
|
+
*/
|
|
157
|
+
buildReason(server, keywords) {
|
|
158
|
+
const matching = keywords.filter((k) => `${server.name} ${server.description || ""}`.toLowerCase().includes(k));
|
|
159
|
+
if (matching.length > 0) {
|
|
160
|
+
return `Related to your recent usage (${matching.slice(0, 3).join(", ")})`;
|
|
161
|
+
}
|
|
162
|
+
return "Popular server that may complement your workflow";
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Build a human-readable reason for an intent-based (query) recommendation.
|
|
166
|
+
*/
|
|
167
|
+
buildIntentReason(server, query, queryTerms) {
|
|
168
|
+
const text = `${server.name} ${server.description || ""}`.toLowerCase();
|
|
169
|
+
const directHits = queryTerms.filter((t) => text.includes(t));
|
|
170
|
+
if (directHits.length > 0) {
|
|
171
|
+
return `Matches "${query}" (terms: ${directHits.slice(0, 3).join(", ")})`;
|
|
172
|
+
}
|
|
173
|
+
return `Surfaced via intent expansion for "${query}"`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=auto-recommender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-recommender.js","sourceRoot":"","sources":["../../src/discovery/auto-recommender.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { GatewayConfig } from "./config/types.js";
|
|
3
|
+
import { RegistryClient } from "./registry/registry-client.js";
|
|
4
|
+
import { ConnectionManager } from "./connection/connection-manager.js";
|
|
5
|
+
import { PermissionManager } from "./permissions/permission-manager.js";
|
|
6
|
+
import { AdTracker } from "./ads/ad-tracker.js";
|
|
7
|
+
import { UsageTracker } from "./analytics/usage-tracker.js";
|
|
8
|
+
import { PluginManager } from "./plugins/plugin-manager.js";
|
|
9
|
+
import { AutoRecommender } from "./discovery/auto-recommender.js";
|
|
10
|
+
import { ManifestResolver } from "./sandbox/manifest-resolver.js";
|
|
11
|
+
import { AuditLog } from "./audit/audit-log.js";
|
|
12
|
+
export interface GatewayContext {
|
|
13
|
+
mcpServer: McpServer;
|
|
14
|
+
connectionManager: ConnectionManager;
|
|
15
|
+
registryClient: RegistryClient;
|
|
16
|
+
permissionManager: PermissionManager;
|
|
17
|
+
adTracker: AdTracker | null;
|
|
18
|
+
usageTracker: UsageTracker | null;
|
|
19
|
+
pluginManager: PluginManager;
|
|
20
|
+
autoRecommender: AutoRecommender | null;
|
|
21
|
+
auditLog: AuditLog;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Process-wide singletons that can be shared across multiple gateway contexts
|
|
25
|
+
* (e.g. one per HTTP session). Sharing these gives the daemon a warm registry
|
|
26
|
+
* cache and a single source of truth for sandbox manifests, while each session
|
|
27
|
+
* still gets its own McpServer + ConnectionManager (connection isolation).
|
|
28
|
+
*/
|
|
29
|
+
export interface SharedSingletons {
|
|
30
|
+
registryClient?: RegistryClient;
|
|
31
|
+
manifestResolver?: ManifestResolver;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create and wire up the MCP Gateway server.
|
|
35
|
+
*
|
|
36
|
+
* Returns a context object containing the McpServer and all component
|
|
37
|
+
* managers. The caller is responsible for connecting a transport and
|
|
38
|
+
* handling shutdown via `connectionManager.disconnectAll()`.
|
|
39
|
+
*
|
|
40
|
+
* @param shared Optional process-wide singletons to reuse across contexts.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createGatewayServer(config: GatewayConfig, shared?: SharedSingletons): GatewayContext;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { InMemoryTaskStore, InMemoryTaskMessageQueue, } from "@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js";
|
|
3
|
+
import { RegistryClient } from "./registry/registry-client.js";
|
|
4
|
+
import { ConnectionManager } from "./connection/connection-manager.js";
|
|
5
|
+
import { PermissionManager } from "./permissions/permission-manager.js";
|
|
6
|
+
import { registerMetaTools } from "./meta-tools/index.js";
|
|
7
|
+
import { AdTracker } from "./ads/ad-tracker.js";
|
|
8
|
+
import { UsageTracker } from "./analytics/usage-tracker.js";
|
|
9
|
+
import { PluginManager } from "./plugins/plugin-manager.js";
|
|
10
|
+
import { loggingPlugin } from "./plugins/builtin/logging-plugin.js";
|
|
11
|
+
import { AutoRecommender } from "./discovery/auto-recommender.js";
|
|
12
|
+
import { ManifestResolver } from "./sandbox/manifest-resolver.js";
|
|
13
|
+
import { AuditLog } from "./audit/audit-log.js";
|
|
14
|
+
import { primeProxyHandlers } from "./proxy/prime-handlers.js";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
const GATEWAY_VERSION = "0.2.0";
|
|
17
|
+
/**
|
|
18
|
+
* Create and wire up the MCP Gateway server.
|
|
19
|
+
*
|
|
20
|
+
* Returns a context object containing the McpServer and all component
|
|
21
|
+
* managers. The caller is responsible for connecting a transport and
|
|
22
|
+
* handling shutdown via `connectionManager.disconnectAll()`.
|
|
23
|
+
*
|
|
24
|
+
* @param shared Optional process-wide singletons to reuse across contexts.
|
|
25
|
+
*/
|
|
26
|
+
export function createGatewayServer(config, shared = {}) {
|
|
27
|
+
const mcpServer = new McpServer({
|
|
28
|
+
name: "mcp-gateway",
|
|
29
|
+
version: GATEWAY_VERSION,
|
|
30
|
+
}, {
|
|
31
|
+
capabilities: {
|
|
32
|
+
tools: { listChanged: true },
|
|
33
|
+
resources: { listChanged: true },
|
|
34
|
+
prompts: { listChanged: true },
|
|
35
|
+
// Experimental Tasks capability — must be advertised at initialize
|
|
36
|
+
// so upstream clients send task-augmented `tools/call` requests.
|
|
37
|
+
// `list`/`cancel` enable tasks/list and tasks/cancel; `requests.tools.call`
|
|
38
|
+
// enables task creation for tool calls.
|
|
39
|
+
tasks: {
|
|
40
|
+
list: {},
|
|
41
|
+
cancel: {},
|
|
42
|
+
requests: { tools: { call: {} } },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
// The in-memory store mirrors downstream task state; the message
|
|
46
|
+
// queue buffers side-channel messages during task execution.
|
|
47
|
+
taskStore: new InMemoryTaskStore(),
|
|
48
|
+
taskMessageQueue: new InMemoryTaskMessageQueue(),
|
|
49
|
+
instructions: [
|
|
50
|
+
"MCP Gateway — a meta-server that discovers, connects, and proxies other MCP servers.",
|
|
51
|
+
"",
|
|
52
|
+
"Use mcp_discover to search the MCP-Rating registry for servers.",
|
|
53
|
+
"Use mcp_connect to connect to a server (spawns it and proxies its tools, resources, and prompts).",
|
|
54
|
+
" - Connect via stdio: mcp_connect({command: 'npx', args: ['-y', 'package']})",
|
|
55
|
+
" - Connect via URL: mcp_connect({url: 'http://localhost:3000/mcp'})",
|
|
56
|
+
" - Connect via slug: mcp_connect({slug: 'server-name'})",
|
|
57
|
+
"Once connected, call the server's tools directly via namespaced names like 'servername__toolname'.",
|
|
58
|
+
"Use mcp_list_active to see all connected servers.",
|
|
59
|
+
"Use mcp_disconnect to remove a server.",
|
|
60
|
+
"",
|
|
61
|
+
"Advanced features:",
|
|
62
|
+
" - mcp_profiles: Manage named connection presets (work, personal, etc.)",
|
|
63
|
+
" - mcp_groups: Atomic connect/disconnect of server groups",
|
|
64
|
+
" - mcp_usage: View call analytics, latency, and error rates",
|
|
65
|
+
" - mcp_recommend: Get AI-powered server suggestions based on usage patterns",
|
|
66
|
+
"",
|
|
67
|
+
"Trust tiers: [Verified] = high quality + official, [Trusted] = good quality,",
|
|
68
|
+
"[Community] = listed in registry, [Unverified] = unknown origin.",
|
|
69
|
+
].join("\n"),
|
|
70
|
+
});
|
|
71
|
+
// Reuse a shared RegistryClient (warm cache) when provided, else create one.
|
|
72
|
+
const registryClient = shared.registryClient ??
|
|
73
|
+
new RegistryClient(config.registryApiUrl, config.registryCacheTtlMs, config.partnerKey);
|
|
74
|
+
const permissionManager = new PermissionManager(config);
|
|
75
|
+
// Create AdTracker if partner key is configured and tracking is not disabled
|
|
76
|
+
const adTracker = config.partnerKey && config.enableAdTracking !== false
|
|
77
|
+
? new AdTracker(config.registryApiUrl, config.partnerKey)
|
|
78
|
+
: null;
|
|
79
|
+
// Create UsageTracker if analytics is enabled
|
|
80
|
+
const usageTracker = config.enableUsageTracking !== false
|
|
81
|
+
? new UsageTracker(config.usageBufferSize)
|
|
82
|
+
: null;
|
|
83
|
+
// Create PluginManager and register built-in plugins
|
|
84
|
+
const pluginManager = new PluginManager();
|
|
85
|
+
pluginManager.register(loggingPlugin);
|
|
86
|
+
// Create ConnectionManager first (AutoRecommender depends on it)
|
|
87
|
+
const connectionManager = new ConnectionManager(mcpServer, registryClient, permissionManager, config, adTracker);
|
|
88
|
+
// Wire up optional components to connection manager
|
|
89
|
+
connectionManager.usageTracker = usageTracker ?? undefined;
|
|
90
|
+
connectionManager.pluginManager = pluginManager;
|
|
91
|
+
// Safety audit trail — forensics log of sandboxed-server actions. Optional
|
|
92
|
+
// durable JSONL alongside the permissions file for SIEM ingestion.
|
|
93
|
+
const auditLog = new AuditLog({
|
|
94
|
+
filePath: config.auditLogPath,
|
|
95
|
+
});
|
|
96
|
+
connectionManager.auditLog = auditLog;
|
|
97
|
+
// Sandbox (L1): per-server capability manifests + env scoping.
|
|
98
|
+
// Manifests are persisted alongside the permissions file. Reuse a shared
|
|
99
|
+
// resolver across sessions when provided (single source of truth on disk).
|
|
100
|
+
const manifestsPath = join(dirname(config.permissionsPath), "mcp-gateway-manifests.json");
|
|
101
|
+
connectionManager.manifestResolver =
|
|
102
|
+
shared.manifestResolver ??
|
|
103
|
+
new ManifestResolver(manifestsPath, config.enableContainerIsolation === true);
|
|
104
|
+
// Create AutoRecommender if enabled (requires connectionManager)
|
|
105
|
+
const autoRecommender = config.enableAutoRecommend !== false
|
|
106
|
+
? new AutoRecommender(registryClient, connectionManager)
|
|
107
|
+
: null;
|
|
108
|
+
// Derive groups path from profiles path
|
|
109
|
+
const groupsPath = config.groupsPath ||
|
|
110
|
+
join(dirname(config.profilesPath), "mcp-gateway-groups.json");
|
|
111
|
+
registerMetaTools(mcpServer, connectionManager, registryClient, {
|
|
112
|
+
adTracker,
|
|
113
|
+
usageTracker,
|
|
114
|
+
autoRecommender,
|
|
115
|
+
auditLog,
|
|
116
|
+
profilesPath: config.profilesPath,
|
|
117
|
+
groupsPath,
|
|
118
|
+
});
|
|
119
|
+
// Prime resource/prompt handlers before the transport connects, so that
|
|
120
|
+
// dynamically-proxied downstream resources/prompts can be registered later
|
|
121
|
+
// without hitting the SDK's post-connect capability lock.
|
|
122
|
+
primeProxyHandlers(mcpServer);
|
|
123
|
+
return {
|
|
124
|
+
mcpServer,
|
|
125
|
+
connectionManager,
|
|
126
|
+
registryClient,
|
|
127
|
+
permissionManager,
|
|
128
|
+
adTracker,
|
|
129
|
+
usageTracker,
|
|
130
|
+
pluginManager,
|
|
131
|
+
autoRecommender,
|
|
132
|
+
auditLog,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=gateway-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-server.js","sourceRoot":"","sources":["../src/gateway-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { GatewayConfig } from "./config/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Run the gateway as a long-running HTTP daemon.
|
|
4
|
+
*
|
|
5
|
+
* Multiple MCP clients connect over Streamable HTTP. Each gets its own
|
|
6
|
+
* isolated session (McpServer + ConnectionManager + downstream connections),
|
|
7
|
+
* but all sessions share a warm RegistryClient cache and a single Manifest
|
|
8
|
+
* resolver (one source of truth for sandbox manifests on disk).
|
|
9
|
+
*
|
|
10
|
+
* Security: every /mcp request must carry `Authorization: Bearer <token>`.
|
|
11
|
+
* The token is required in daemon mode regardless of bind address.
|
|
12
|
+
*
|
|
13
|
+
* Multi-tenant: supply `tokens` (tenantId → token) to run a shared daemon
|
|
14
|
+
* where each tenant authenticates with its own token. Sessions are bound to
|
|
15
|
+
* the authenticating tenant (cross-tenant session reuse is rejected), and each
|
|
16
|
+
* tenant gets an isolated sandbox-manifest store. The single `token` maps to
|
|
17
|
+
* tenant "default" for backward compatibility.
|
|
18
|
+
*/
|
|
19
|
+
export declare function runHttpDaemon(config: GatewayConfig, opts: {
|
|
20
|
+
port: number;
|
|
21
|
+
host: string;
|
|
22
|
+
token?: string;
|
|
23
|
+
tokens?: Record<string, string>;
|
|
24
|
+
allowedHosts?: string[];
|
|
25
|
+
allowedOrigins?: string[];
|
|
26
|
+
}): Promise<void>;
|