@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 [YOUR FULL NAME]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @mcp-rating/gateway
|
|
2
|
+
|
|
3
|
+
**MCP Gateway** is a meta-MCP server that auto-discovers, connects, and proxies other MCP servers. Add one line to your AI desktop client config and get dynamic access to the entire MCP ecosystem.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌────────────────────┐ ┌──────────────┐ ┌──────────────────┐
|
|
9
|
+
│ Claude Desktop / │ stdio │ │ stdio │ MCP Server A │
|
|
10
|
+
│ Cursor / Windsurf │◄─────►│ MCP Gateway │◄──────►│ (e.g. filesystem)│
|
|
11
|
+
│ (host client) │ │ │◄──┐ └──────────────────┘
|
|
12
|
+
└────────────────────┘ └──────────────┘ │ ┌──────────────────┐
|
|
13
|
+
│ └───►│ MCP Server B │
|
|
14
|
+
▼ │ (e.g. github) │
|
|
15
|
+
┌──────────────┐ └──────────────────┘
|
|
16
|
+
│ MCP-Rating │
|
|
17
|
+
│ Registry API │
|
|
18
|
+
└──────────────┘
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Instead of manually configuring each MCP server in your client, the Gateway:
|
|
22
|
+
|
|
23
|
+
1. **Discovers** servers via the MCP-Rating registry
|
|
24
|
+
2. **Connects** to them on-demand (spawns as child processes)
|
|
25
|
+
3. **Proxies** their tools through namespaced names (`servername__toolname`)
|
|
26
|
+
4. **Notifies** your client when tools are added/removed
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### With Claude Desktop
|
|
31
|
+
|
|
32
|
+
Add to your `claude_desktop_config.json`:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"mcpServers": {
|
|
37
|
+
"gateway": {
|
|
38
|
+
"command": "npx",
|
|
39
|
+
"args": ["-y", "@mcp-rating/gateway"]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then ask Claude:
|
|
46
|
+
- *"Search for MCP servers that work with databases"* (uses `mcp_discover`)
|
|
47
|
+
- *"Connect to the sqlite server"* (uses `mcp_connect`)
|
|
48
|
+
- *"Query my database"* (calls the proxied tool directly)
|
|
49
|
+
|
|
50
|
+
### With Any MCP Client
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"command": "npx",
|
|
55
|
+
"args": ["-y", "@mcp-rating/gateway"]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Meta-Tools
|
|
60
|
+
|
|
61
|
+
The gateway exposes 5 built-in tools:
|
|
62
|
+
|
|
63
|
+
| Tool | Description |
|
|
64
|
+
|------|-------------|
|
|
65
|
+
| `mcp_discover` | Search the MCP-Rating registry for servers |
|
|
66
|
+
| `mcp_connect` | Connect to a server (by slug or explicit command) |
|
|
67
|
+
| `mcp_disconnect` | Disconnect a server and remove its tools |
|
|
68
|
+
| `mcp_list_active` | List all connected servers |
|
|
69
|
+
| `mcp_server_info` | Get detailed info about a server |
|
|
70
|
+
|
|
71
|
+
## Trust Tiers
|
|
72
|
+
|
|
73
|
+
Every connected server is labeled with a trust tier based on its MCP-Rating quality score:
|
|
74
|
+
|
|
75
|
+
- **[Verified]** — High quality + officially verified
|
|
76
|
+
- **[Trusted]** — Good quality with repository and install command
|
|
77
|
+
- **[Community]** — Listed in registry with basic quality
|
|
78
|
+
- **[Unverified]** — Unknown origin (manually connected)
|
|
79
|
+
|
|
80
|
+
## Configuration
|
|
81
|
+
|
|
82
|
+
The gateway reads config from `~/.mcp-gateway/config.json`:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"registryApiUrl": "https://mcp-rating.example.com/api/v1",
|
|
87
|
+
"proxyTimeoutMs": 30000,
|
|
88
|
+
"maxConnections": 10,
|
|
89
|
+
"logLevel": "info"
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Environment Variables
|
|
94
|
+
|
|
95
|
+
| Variable | Description | Default |
|
|
96
|
+
|----------|-------------|---------|
|
|
97
|
+
| `MCP_GATEWAY_REGISTRY_URL` | MCP-Rating API base URL | `https://mcprating.io/api/v1` |
|
|
98
|
+
| `MCP_GATEWAY_TIMEOUT` | Proxy timeout (ms) | `30000` |
|
|
99
|
+
| `MCP_GATEWAY_MAX_CONNECTIONS` | Max simultaneous connections | `10` |
|
|
100
|
+
| `MCP_GATEWAY_LOG_LEVEL` | Log level (debug/info/warn/error) | `info` |
|
|
101
|
+
|
|
102
|
+
## Development
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Install dependencies
|
|
106
|
+
pnpm install
|
|
107
|
+
|
|
108
|
+
# Run in development mode
|
|
109
|
+
pnpm dev
|
|
110
|
+
|
|
111
|
+
# Type check
|
|
112
|
+
pnpm typecheck
|
|
113
|
+
|
|
114
|
+
# Build
|
|
115
|
+
pnpm build
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Architecture
|
|
119
|
+
|
|
120
|
+
The gateway is built on the [MCP SDK](https://github.com/modelcontextprotocol/typescript-sdk) and uses:
|
|
121
|
+
|
|
122
|
+
- **StdioServerTransport** — communicates with the host client
|
|
123
|
+
- **StdioClientTransport** — spawns and communicates with downstream servers
|
|
124
|
+
- **Dynamic tool registration** — `McpServer.registerTool()` + `sendToolListChanged()`
|
|
125
|
+
- **Tool namespacing** — `slug__toolname` pattern prevents collisions
|
|
126
|
+
- **Passthrough Zod schemas** — preserves parameter names for host client UI while letting downstream servers validate
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
ISC
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface AdEvent {
|
|
2
|
+
serverSlug: string;
|
|
3
|
+
eventType: "connect" | "tool_usage";
|
|
4
|
+
toolName?: string;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
timestamp: Date;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Tracks connect and tool-usage events for promoted servers.
|
|
10
|
+
* Reports events back to the MCP-Rating API via fire-and-forget HTTP POST.
|
|
11
|
+
*/
|
|
12
|
+
export declare class AdTracker {
|
|
13
|
+
private readonly registryApiUrl;
|
|
14
|
+
private readonly partnerKey;
|
|
15
|
+
/** Unique session ID for this gateway instance */
|
|
16
|
+
readonly sessionId: string;
|
|
17
|
+
/** In-memory event log for diagnostics */
|
|
18
|
+
private events;
|
|
19
|
+
/** Count of tracked events by type */
|
|
20
|
+
private eventCounts;
|
|
21
|
+
constructor(registryApiUrl: string, partnerKey: string);
|
|
22
|
+
/**
|
|
23
|
+
* Track a server connect event.
|
|
24
|
+
* Records as high-value click (click_type: 'install') in the ad network.
|
|
25
|
+
*/
|
|
26
|
+
trackConnect(serverSlug: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Track a proxied tool usage event.
|
|
29
|
+
* Records as action (CPA event) in the ad network.
|
|
30
|
+
*/
|
|
31
|
+
trackToolUsage(serverSlug: string, toolName: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Get partner earnings from the registry API.
|
|
34
|
+
*/
|
|
35
|
+
getPartnerEarnings(): Promise<{
|
|
36
|
+
totalGrossCents: number;
|
|
37
|
+
totalPartnerShareCents: number;
|
|
38
|
+
eventCount: number;
|
|
39
|
+
} | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Get diagnostics info for the ad status meta-tool.
|
|
42
|
+
*/
|
|
43
|
+
getStatus(): {
|
|
44
|
+
sessionId: string;
|
|
45
|
+
partnerKeyConfigured: boolean;
|
|
46
|
+
eventCounts: {
|
|
47
|
+
connect: number;
|
|
48
|
+
tool_usage: number;
|
|
49
|
+
};
|
|
50
|
+
totalEvents: number;
|
|
51
|
+
recentEvents: {
|
|
52
|
+
serverSlug: string;
|
|
53
|
+
eventType: "connect" | "tool_usage";
|
|
54
|
+
toolName: string | undefined;
|
|
55
|
+
timestamp: string;
|
|
56
|
+
}[];
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Report an ad event to the MCP-Rating API.
|
|
60
|
+
* Fire-and-forget — errors are logged but don't propagate.
|
|
61
|
+
*/
|
|
62
|
+
private reportEvent;
|
|
63
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { log } from "../utils/logger.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
/**
|
|
4
|
+
* Tracks connect and tool-usage events for promoted servers.
|
|
5
|
+
* Reports events back to the MCP-Rating API via fire-and-forget HTTP POST.
|
|
6
|
+
*/
|
|
7
|
+
export class AdTracker {
|
|
8
|
+
registryApiUrl;
|
|
9
|
+
partnerKey;
|
|
10
|
+
/** Unique session ID for this gateway instance */
|
|
11
|
+
sessionId;
|
|
12
|
+
/** In-memory event log for diagnostics */
|
|
13
|
+
events = [];
|
|
14
|
+
/** Count of tracked events by type */
|
|
15
|
+
eventCounts = { connect: 0, tool_usage: 0 };
|
|
16
|
+
constructor(registryApiUrl, partnerKey) {
|
|
17
|
+
this.registryApiUrl = registryApiUrl;
|
|
18
|
+
this.partnerKey = partnerKey;
|
|
19
|
+
this.sessionId = randomUUID();
|
|
20
|
+
log.info("Ad tracker initialized", {
|
|
21
|
+
sessionId: this.sessionId,
|
|
22
|
+
registryApiUrl,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Track a server connect event.
|
|
27
|
+
* Records as high-value click (click_type: 'install') in the ad network.
|
|
28
|
+
*/
|
|
29
|
+
async trackConnect(serverSlug) {
|
|
30
|
+
this.events.push({
|
|
31
|
+
serverSlug,
|
|
32
|
+
eventType: "connect",
|
|
33
|
+
sessionId: this.sessionId,
|
|
34
|
+
timestamp: new Date(),
|
|
35
|
+
});
|
|
36
|
+
this.eventCounts.connect++;
|
|
37
|
+
// Trim in-memory log to last 500 events
|
|
38
|
+
if (this.events.length > 500) {
|
|
39
|
+
this.events = this.events.slice(-500);
|
|
40
|
+
}
|
|
41
|
+
// Fire-and-forget report to API
|
|
42
|
+
this.reportEvent("install", serverSlug).catch((err) => {
|
|
43
|
+
log.debug("Failed to report connect event", {
|
|
44
|
+
serverSlug,
|
|
45
|
+
error: String(err),
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Track a proxied tool usage event.
|
|
51
|
+
* Records as action (CPA event) in the ad network.
|
|
52
|
+
*/
|
|
53
|
+
async trackToolUsage(serverSlug, toolName) {
|
|
54
|
+
this.events.push({
|
|
55
|
+
serverSlug,
|
|
56
|
+
eventType: "tool_usage",
|
|
57
|
+
toolName,
|
|
58
|
+
sessionId: this.sessionId,
|
|
59
|
+
timestamp: new Date(),
|
|
60
|
+
});
|
|
61
|
+
this.eventCounts.tool_usage++;
|
|
62
|
+
// Trim in-memory log
|
|
63
|
+
if (this.events.length > 500) {
|
|
64
|
+
this.events = this.events.slice(-500);
|
|
65
|
+
}
|
|
66
|
+
// Fire-and-forget report to API
|
|
67
|
+
this.reportEvent("action_completed", serverSlug).catch((err) => {
|
|
68
|
+
log.debug("Failed to report tool usage event", {
|
|
69
|
+
serverSlug,
|
|
70
|
+
toolName,
|
|
71
|
+
error: String(err),
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get partner earnings from the registry API.
|
|
77
|
+
*/
|
|
78
|
+
async getPartnerEarnings() {
|
|
79
|
+
try {
|
|
80
|
+
const res = await fetch(`${this.registryApiUrl}/partners/me/revenue`, {
|
|
81
|
+
headers: {
|
|
82
|
+
"x-partner-key": this.partnerKey,
|
|
83
|
+
},
|
|
84
|
+
signal: AbortSignal.timeout(5_000),
|
|
85
|
+
});
|
|
86
|
+
if (!res.ok)
|
|
87
|
+
return null;
|
|
88
|
+
const data = (await res.json());
|
|
89
|
+
return {
|
|
90
|
+
totalGrossCents: data.totalGross,
|
|
91
|
+
totalPartnerShareCents: data.totalPartnerShare,
|
|
92
|
+
eventCount: data.count,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get diagnostics info for the ad status meta-tool.
|
|
101
|
+
*/
|
|
102
|
+
getStatus() {
|
|
103
|
+
return {
|
|
104
|
+
sessionId: this.sessionId,
|
|
105
|
+
partnerKeyConfigured: true,
|
|
106
|
+
eventCounts: { ...this.eventCounts },
|
|
107
|
+
totalEvents: this.events.length,
|
|
108
|
+
recentEvents: this.events.slice(-10).map((e) => ({
|
|
109
|
+
serverSlug: e.serverSlug,
|
|
110
|
+
eventType: e.eventType,
|
|
111
|
+
toolName: e.toolName,
|
|
112
|
+
timestamp: e.timestamp.toISOString(),
|
|
113
|
+
})),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Report an ad event to the MCP-Rating API.
|
|
118
|
+
* Fire-and-forget — errors are logged but don't propagate.
|
|
119
|
+
*/
|
|
120
|
+
async reportEvent(clickType, serverSlug) {
|
|
121
|
+
const url = `${this.registryApiUrl}/partners/report-click`;
|
|
122
|
+
const res = await fetch(url, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: {
|
|
125
|
+
"Content-Type": "application/json",
|
|
126
|
+
"x-partner-key": this.partnerKey,
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify({
|
|
129
|
+
serverSlug,
|
|
130
|
+
clickType,
|
|
131
|
+
sessionId: this.sessionId,
|
|
132
|
+
}),
|
|
133
|
+
signal: AbortSignal.timeout(5_000),
|
|
134
|
+
});
|
|
135
|
+
if (!res.ok) {
|
|
136
|
+
log.debug("Ad event report failed", {
|
|
137
|
+
status: res.status,
|
|
138
|
+
clickType,
|
|
139
|
+
serverSlug,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=ad-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ad-tracker.js","sourceRoot":"","sources":["../../src/ads/ad-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export interface ToolCallEvent {
|
|
2
|
+
slug: string;
|
|
3
|
+
toolName: string;
|
|
4
|
+
startedAt: number;
|
|
5
|
+
durationMs: number;
|
|
6
|
+
success: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ToolAggregates {
|
|
10
|
+
key: string;
|
|
11
|
+
callCount: number;
|
|
12
|
+
errorCount: number;
|
|
13
|
+
totalDurationMs: number;
|
|
14
|
+
avgLatencyMs: number;
|
|
15
|
+
p95LatencyMs: number;
|
|
16
|
+
lastCalledAt: number;
|
|
17
|
+
/** Recent latencies for p95 calculation (last 100) */
|
|
18
|
+
latencies: number[];
|
|
19
|
+
}
|
|
20
|
+
export interface ServerAggregates {
|
|
21
|
+
slug: string;
|
|
22
|
+
totalCalls: number;
|
|
23
|
+
totalErrors: number;
|
|
24
|
+
avgLatencyMs: number;
|
|
25
|
+
}
|
|
26
|
+
export interface UsageStats {
|
|
27
|
+
totalCalls: number;
|
|
28
|
+
totalErrors: number;
|
|
29
|
+
avgLatencyMs: number;
|
|
30
|
+
topToolsByCalls: ToolAggregates[];
|
|
31
|
+
slowestTools: ToolAggregates[];
|
|
32
|
+
perServer: ServerAggregates[];
|
|
33
|
+
recentCalls: ToolCallEvent[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* In-memory usage analytics tracker.
|
|
37
|
+
* Uses a ring buffer for events and maintains per-tool aggregates.
|
|
38
|
+
*/
|
|
39
|
+
export declare class UsageTracker {
|
|
40
|
+
private events;
|
|
41
|
+
private maxEvents;
|
|
42
|
+
private insertIdx;
|
|
43
|
+
private totalInserted;
|
|
44
|
+
/** Per-tool aggregates keyed by "slug::toolName" */
|
|
45
|
+
private toolAggregates;
|
|
46
|
+
constructor(bufferSize?: number);
|
|
47
|
+
/**
|
|
48
|
+
* Record a completed tool call event.
|
|
49
|
+
*/
|
|
50
|
+
recordCall(event: ToolCallEvent): void;
|
|
51
|
+
/**
|
|
52
|
+
* Get overall usage statistics.
|
|
53
|
+
*/
|
|
54
|
+
getStats(slug?: string): UsageStats;
|
|
55
|
+
/**
|
|
56
|
+
* Get the most recent tool call events.
|
|
57
|
+
*/
|
|
58
|
+
getRecentCalls(limit?: number): ToolCallEvent[];
|
|
59
|
+
/**
|
|
60
|
+
* Get tool calls that exceeded a latency threshold.
|
|
61
|
+
*/
|
|
62
|
+
getSlowCalls(thresholdMs?: number): ToolCallEvent[];
|
|
63
|
+
/**
|
|
64
|
+
* Reset all analytics data.
|
|
65
|
+
*/
|
|
66
|
+
reset(): void;
|
|
67
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { log } from "../utils/logger.js";
|
|
2
|
+
// ── Usage Tracker ────────────────────────────────────────────────────────────
|
|
3
|
+
const DEFAULT_BUFFER_SIZE = 10_000;
|
|
4
|
+
const LATENCY_WINDOW = 100; // Keep last 100 latencies for p95 calculation
|
|
5
|
+
/**
|
|
6
|
+
* In-memory usage analytics tracker.
|
|
7
|
+
* Uses a ring buffer for events and maintains per-tool aggregates.
|
|
8
|
+
*/
|
|
9
|
+
export class UsageTracker {
|
|
10
|
+
events = [];
|
|
11
|
+
maxEvents;
|
|
12
|
+
insertIdx = 0;
|
|
13
|
+
totalInserted = 0;
|
|
14
|
+
/** Per-tool aggregates keyed by "slug::toolName" */
|
|
15
|
+
toolAggregates = new Map();
|
|
16
|
+
constructor(bufferSize = DEFAULT_BUFFER_SIZE) {
|
|
17
|
+
this.maxEvents = bufferSize;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Record a completed tool call event.
|
|
21
|
+
*/
|
|
22
|
+
recordCall(event) {
|
|
23
|
+
// Ring buffer insert
|
|
24
|
+
if (this.totalInserted < this.maxEvents) {
|
|
25
|
+
this.events.push(event);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.events[this.insertIdx] = event;
|
|
29
|
+
}
|
|
30
|
+
this.insertIdx = (this.insertIdx + 1) % this.maxEvents;
|
|
31
|
+
this.totalInserted++;
|
|
32
|
+
// Update aggregates
|
|
33
|
+
const key = `${event.slug}::${event.toolName}`;
|
|
34
|
+
let agg = this.toolAggregates.get(key);
|
|
35
|
+
if (!agg) {
|
|
36
|
+
agg = {
|
|
37
|
+
key,
|
|
38
|
+
callCount: 0,
|
|
39
|
+
errorCount: 0,
|
|
40
|
+
totalDurationMs: 0,
|
|
41
|
+
avgLatencyMs: 0,
|
|
42
|
+
p95LatencyMs: 0,
|
|
43
|
+
lastCalledAt: 0,
|
|
44
|
+
latencies: [],
|
|
45
|
+
};
|
|
46
|
+
this.toolAggregates.set(key, agg);
|
|
47
|
+
}
|
|
48
|
+
agg.callCount++;
|
|
49
|
+
if (!event.success)
|
|
50
|
+
agg.errorCount++;
|
|
51
|
+
agg.totalDurationMs += event.durationMs;
|
|
52
|
+
agg.avgLatencyMs = agg.totalDurationMs / agg.callCount;
|
|
53
|
+
agg.lastCalledAt = event.startedAt;
|
|
54
|
+
// Track latencies for p95 (rolling window)
|
|
55
|
+
agg.latencies.push(event.durationMs);
|
|
56
|
+
if (agg.latencies.length > LATENCY_WINDOW) {
|
|
57
|
+
agg.latencies.shift();
|
|
58
|
+
}
|
|
59
|
+
agg.p95LatencyMs = percentile(agg.latencies, 95);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get overall usage statistics.
|
|
63
|
+
*/
|
|
64
|
+
getStats(slug) {
|
|
65
|
+
const allAggregates = [...this.toolAggregates.values()];
|
|
66
|
+
const filtered = slug
|
|
67
|
+
? allAggregates.filter((a) => a.key.startsWith(`${slug}::`))
|
|
68
|
+
: allAggregates;
|
|
69
|
+
const totalCalls = filtered.reduce((s, a) => s + a.callCount, 0);
|
|
70
|
+
const totalErrors = filtered.reduce((s, a) => s + a.errorCount, 0);
|
|
71
|
+
const totalDuration = filtered.reduce((s, a) => s + a.totalDurationMs, 0);
|
|
72
|
+
const avgLatencyMs = totalCalls > 0 ? totalDuration / totalCalls : 0;
|
|
73
|
+
// Top tools by call count
|
|
74
|
+
const topToolsByCalls = [...filtered]
|
|
75
|
+
.sort((a, b) => b.callCount - a.callCount)
|
|
76
|
+
.slice(0, 10);
|
|
77
|
+
// Slowest tools by avg latency (min 5 calls to be relevant)
|
|
78
|
+
const slowestTools = [...filtered]
|
|
79
|
+
.filter((a) => a.callCount >= 5)
|
|
80
|
+
.sort((a, b) => b.avgLatencyMs - a.avgLatencyMs)
|
|
81
|
+
.slice(0, 10);
|
|
82
|
+
// Per-server aggregates
|
|
83
|
+
const serverMap = new Map();
|
|
84
|
+
for (const agg of filtered) {
|
|
85
|
+
const aggSlug = agg.key.split("::")[0];
|
|
86
|
+
const server = serverMap.get(aggSlug) || { totalCalls: 0, totalErrors: 0, totalDuration: 0 };
|
|
87
|
+
server.totalCalls += agg.callCount;
|
|
88
|
+
server.totalErrors += agg.errorCount;
|
|
89
|
+
server.totalDuration += agg.totalDurationMs;
|
|
90
|
+
serverMap.set(aggSlug, server);
|
|
91
|
+
}
|
|
92
|
+
const perServer = [...serverMap.entries()].map(([s, v]) => ({
|
|
93
|
+
slug: s,
|
|
94
|
+
totalCalls: v.totalCalls,
|
|
95
|
+
totalErrors: v.totalErrors,
|
|
96
|
+
avgLatencyMs: v.totalCalls > 0 ? v.totalDuration / v.totalCalls : 0,
|
|
97
|
+
}));
|
|
98
|
+
return {
|
|
99
|
+
totalCalls,
|
|
100
|
+
totalErrors,
|
|
101
|
+
avgLatencyMs,
|
|
102
|
+
topToolsByCalls,
|
|
103
|
+
slowestTools,
|
|
104
|
+
perServer,
|
|
105
|
+
recentCalls: this.getRecentCalls(20),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get the most recent tool call events.
|
|
110
|
+
*/
|
|
111
|
+
getRecentCalls(limit = 20) {
|
|
112
|
+
const len = Math.min(this.events.length, limit);
|
|
113
|
+
const recent = [];
|
|
114
|
+
// Read backwards from the insertion point
|
|
115
|
+
for (let i = 0; i < len; i++) {
|
|
116
|
+
const idx = (this.insertIdx - 1 - i + this.events.length) % this.events.length;
|
|
117
|
+
if (idx >= 0 && idx < this.events.length) {
|
|
118
|
+
recent.push(this.events[idx]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return recent;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get tool calls that exceeded a latency threshold.
|
|
125
|
+
*/
|
|
126
|
+
getSlowCalls(thresholdMs = 5000) {
|
|
127
|
+
return this.events.filter((e) => e.durationMs >= thresholdMs);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Reset all analytics data.
|
|
131
|
+
*/
|
|
132
|
+
reset() {
|
|
133
|
+
this.events = [];
|
|
134
|
+
this.insertIdx = 0;
|
|
135
|
+
this.totalInserted = 0;
|
|
136
|
+
this.toolAggregates.clear();
|
|
137
|
+
log.info("Usage analytics reset");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
141
|
+
function percentile(values, p) {
|
|
142
|
+
if (values.length === 0)
|
|
143
|
+
return 0;
|
|
144
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
145
|
+
const idx = Math.ceil((p / 100) * sorted.length) - 1;
|
|
146
|
+
return sorted[Math.max(0, idx)];
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=usage-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-tracker.js","sourceRoot":"","sources":["../../src/analytics/usage-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety audit trail — a forensics record of what sandboxed MCP servers
|
|
3
|
+
* actually did, and what the sandbox blocked.
|
|
4
|
+
*
|
|
5
|
+
* This is deliberately NOT generic observability (tool-call telemetry exists
|
|
6
|
+
* in UsageTracker). It's a *security* artifact: the evidence that containment
|
|
7
|
+
* worked. A security team reviewing "did this untrusted server try anything?"
|
|
8
|
+
* reads this log — it records sandbox-relevant events (calls under an
|
|
9
|
+
* enforcement level, blocked egress, blocked tools, connects with their
|
|
10
|
+
* granted capability envelope), not performance metrics.
|
|
11
|
+
*
|
|
12
|
+
* Storage: an in-memory ring buffer (always) plus optional append-only JSONL
|
|
13
|
+
* file (for durable forensics / SIEM ingestion).
|
|
14
|
+
*/
|
|
15
|
+
export type AuditEventType = "connect" | "tool_call" | "tool_blocked" | "egress_blocked" | "egress_allowed" | "disconnect";
|
|
16
|
+
export interface AuditEvent {
|
|
17
|
+
/** ISO timestamp. */
|
|
18
|
+
ts: string;
|
|
19
|
+
type: AuditEventType;
|
|
20
|
+
/** Downstream server slug the event concerns. */
|
|
21
|
+
slug: string;
|
|
22
|
+
/** Sandbox enforcement level in effect (none | l1-process | l2-container | l3-wasm). */
|
|
23
|
+
enforcement?: string;
|
|
24
|
+
/** Tool name, for tool_* events. */
|
|
25
|
+
tool?: string;
|
|
26
|
+
/** Target host:port, for egress_* events. */
|
|
27
|
+
target?: string;
|
|
28
|
+
/** Whether the action succeeded (tool_call) — false on downstream error. */
|
|
29
|
+
ok?: boolean;
|
|
30
|
+
/** Short reason (block cause, error class). Never contains args/secrets. */
|
|
31
|
+
reason?: string;
|
|
32
|
+
/** Session id (multi-tenant daemon), if available. */
|
|
33
|
+
sessionId?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface AuditLogOptions {
|
|
36
|
+
/** Max events kept in memory (ring buffer). Default 5000. */
|
|
37
|
+
bufferSize?: number;
|
|
38
|
+
/** Optional JSONL file path for durable, append-only forensics. */
|
|
39
|
+
filePath?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Append-only safety audit log. Recording never throws — a forensics logger
|
|
43
|
+
* must not break the thing it observes.
|
|
44
|
+
*/
|
|
45
|
+
export declare class AuditLog {
|
|
46
|
+
private readonly buffer;
|
|
47
|
+
private readonly bufferSize;
|
|
48
|
+
private readonly filePath?;
|
|
49
|
+
private counts;
|
|
50
|
+
constructor(opts?: AuditLogOptions);
|
|
51
|
+
/** Record an event. Caller supplies everything except the timestamp. */
|
|
52
|
+
record(event: Omit<AuditEvent, "ts">): void;
|
|
53
|
+
/**
|
|
54
|
+
* Query recent events, newest first. Filterable by slug, type, and a
|
|
55
|
+
* "security-relevant only" flag (blocks + errors — the events a reviewer
|
|
56
|
+
* actually cares about).
|
|
57
|
+
*/
|
|
58
|
+
query(opts?: {
|
|
59
|
+
slug?: string;
|
|
60
|
+
type?: AuditEventType;
|
|
61
|
+
securityRelevantOnly?: boolean;
|
|
62
|
+
limit?: number;
|
|
63
|
+
}): AuditEvent[];
|
|
64
|
+
/** Aggregate counts by event type (for the summary view). */
|
|
65
|
+
summary(): {
|
|
66
|
+
counts: Record<AuditEventType, number>;
|
|
67
|
+
total: number;
|
|
68
|
+
/** Count of security-relevant events (blocks + errors). */
|
|
69
|
+
securityEvents: number;
|
|
70
|
+
};
|
|
71
|
+
}
|