@mcp-guardian/server 0.4.0 → 0.7.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/README.md +119 -8
- package/dist/auth/auth-types.d.ts +40 -0
- package/dist/auth/auth-types.d.ts.map +1 -0
- package/dist/auth/auth-types.js +5 -0
- package/dist/auth/auth-types.js.map +1 -0
- package/dist/auth/oauth.d.ts +25 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +96 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/redis-session-cache.d.ts +21 -0
- package/dist/auth/redis-session-cache.d.ts.map +1 -0
- package/dist/auth/redis-session-cache.js +74 -0
- package/dist/auth/redis-session-cache.js.map +1 -0
- package/dist/auth/session-cache.d.ts +47 -0
- package/dist/auth/session-cache.d.ts.map +1 -0
- package/dist/auth/session-cache.js +91 -0
- package/dist/auth/session-cache.js.map +1 -0
- package/dist/cli.js +27 -5
- package/dist/cli.js.map +1 -1
- package/dist/database/database-interface.d.ts +17 -0
- package/dist/database/database-interface.d.ts.map +1 -0
- package/dist/database/database-interface.js +2 -0
- package/dist/database/database-interface.js.map +1 -0
- package/dist/database/history-db.d.ts.map +1 -1
- package/dist/database/history-db.js.map +1 -1
- package/dist/database/postgres-db.d.ts +18 -0
- package/dist/database/postgres-db.d.ts.map +1 -0
- package/dist/database/postgres-db.js +118 -0
- package/dist/database/postgres-db.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/policy/policy-engine.d.ts.map +1 -1
- package/dist/policy/policy-engine.js +21 -0
- package/dist/policy/policy-engine.js.map +1 -1
- package/dist/policy/policy-types.d.ts +9 -0
- package/dist/policy/policy-types.d.ts.map +1 -1
- package/dist/policy/policy-watcher.d.ts +24 -0
- package/dist/policy/policy-watcher.d.ts.map +1 -0
- package/dist/policy/policy-watcher.js +68 -0
- package/dist/policy/policy-watcher.js.map +1 -0
- package/dist/proxy/proxy-manager.d.ts +3 -1
- package/dist/proxy/proxy-manager.d.ts.map +1 -1
- package/dist/proxy/proxy-manager.js +10 -3
- package/dist/proxy/proxy-manager.js.map +1 -1
- package/dist/proxy/proxy-server.d.ts +20 -8
- package/dist/proxy/proxy-server.d.ts.map +1 -1
- package/dist/proxy/proxy-server.js +209 -37
- package/dist/proxy/proxy-server.js.map +1 -1
- package/dist/utils/circuit-breaker.d.ts +29 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +81 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/metrics.d.ts +10 -0
- package/dist/utils/metrics.d.ts.map +1 -0
- package/dist/utils/metrics.js +77 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/structured-logger.d.ts +1 -1
- package/dist/utils/structured-logger.d.ts.map +1 -1
- package/package.json +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-manager.js","sourceRoot":"","sources":["../../src/proxy/proxy-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy-manager.js","sourceRoot":"","sources":["../../src/proxy/proxy-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAI5C,MAAM,OAAO,YAAY;IAIb;IACA;IACA;IALF,OAAO,GAAqB,EAAE,CAAC;IAEvC,YACU,EAAmB,EACnB,YAA2B,EAC3B,aAA8B;QAF9B,OAAE,GAAF,EAAE,CAAiB;QACnB,iBAAY,GAAZ,YAAY,CAAe;QAC3B,kBAAa,GAAb,aAAa,CAAiB;IACrC,CAAC;IAEJ,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAA0B;QACvC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,SAAS,KAAK,OAAO,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnD,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,IAAI,cAAc,CAC9B,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,IAAI,IAAI,EAAE,EACjB,MAAM,CAAC,GAAG,IAAI,EAAE,EAChB,IAAI,CAAC,EAAE,EACP,MAAM,CAAC,IAAI,EACX,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,aAAa,CACnB,CAAC;oBACF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACzB,MAAM,MAAM,GAAa,EAAE,CAAC;oBAC5B,IAAI,IAAI,CAAC,YAAY;wBAAE,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;oBAC7E,IAAI,IAAI,CAAC,aAAa;wBAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;oBACvD,MAAM,CAAC,IAAI,CAAC,qBAAqB,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvH,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBAClB,MAAM,CAAC,KAAK,CAAC,6BAA6B,MAAM,CAAC,IAAI,KAAK,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC5E,CAAC;YACH,CAAC;iBAAM,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,IAAI,+BAA+B,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACrC,CAAC;CACF"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { HistoryDatabase } from '../database/history-db.js';
|
|
2
2
|
import { PolicyEngine } from '../policy/policy-engine.js';
|
|
3
|
+
import { OAuthValidator } from '../auth/oauth.js';
|
|
3
4
|
/**
|
|
4
|
-
* MCP Proxy Interceptor — sits between the AI client and an MCP server
|
|
5
|
-
* capturing every JSON-RPC call's token usage for real cost auditing.
|
|
5
|
+
* MCP Proxy Interceptor — sits between the AI client and an MCP server.
|
|
6
6
|
*
|
|
7
7
|
* v0.4: Integrated PolicyEngine for active blocking of malicious tool calls.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* v0.5: OAuth 2.1 JWT validation — validates bearer tokens before policy evaluation.
|
|
9
|
+
* v0.5.2: Circuit breaker for upstream MCP server failures.
|
|
10
|
+
* v0.5.2: Per-client rate limiting (keyed by agent sub + tool name).
|
|
11
|
+
* v0.5.2: Consistent SIEM fields (request_id, proxy_latency_ms, authn_success, authz_allowed).
|
|
10
12
|
*/
|
|
11
13
|
export declare class McpProxyServer {
|
|
12
14
|
private child;
|
|
@@ -19,16 +21,26 @@ export declare class McpProxyServer {
|
|
|
19
21
|
private requestArguments;
|
|
20
22
|
private serverName;
|
|
21
23
|
private policyEngine;
|
|
22
|
-
|
|
24
|
+
private authValidator;
|
|
25
|
+
private sessionCache;
|
|
26
|
+
private circuitBreaker;
|
|
27
|
+
/** v0.5.2: Per-client rate limit counters (key: agentSub:toolName) */
|
|
28
|
+
private clientRateCounters;
|
|
29
|
+
constructor(command: string, args: string[], env: Record<string, string>, db: HistoryDatabase, serverName?: string, policyEngine?: PolicyEngine, authValidator?: OAuthValidator);
|
|
23
30
|
get stdin(): NodeJS.WritableStream | null;
|
|
24
31
|
private setupStdout;
|
|
25
32
|
private setupStderr;
|
|
33
|
+
private sendError;
|
|
26
34
|
/**
|
|
27
35
|
* Called when the AI client writes a request to be proxied.
|
|
28
|
-
*
|
|
29
|
-
* Blocked calls receive a JSON-RPC error response.
|
|
36
|
+
* Pipeline: Auth → Circuit Breaker → Policy + RBAC → Forward.
|
|
30
37
|
*/
|
|
31
|
-
handleClientInput(raw: string): void
|
|
38
|
+
handleClientInput(raw: string): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Check per-client rate limits against RBAC rules.
|
|
41
|
+
* Returns block reason string or null.
|
|
42
|
+
*/
|
|
43
|
+
private checkPerClientRateLimit;
|
|
32
44
|
kill(): void;
|
|
33
45
|
}
|
|
34
46
|
//# sourceMappingURL=proxy-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-server.d.ts","sourceRoot":"","sources":["../../src/proxy/proxy-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"proxy-server.d.ts","sourceRoot":"","sources":["../../src/proxy/proxy-server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAMlD;;;;;;;;GAQG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,EAAE,CAAkB;IAC5B,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,gBAAgB,CAAsC;IAC9D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,cAAc,CAAiB;IACvC,sEAAsE;IACtE,OAAO,CAAC,kBAAkB,CAA8D;gBAGtF,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,EAAE,EAAE,eAAe,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,YAAY,EAC3B,aAAa,CAAC,EAAE,cAAc;IA0BhC,IAAI,KAAK,IAAI,MAAM,CAAC,cAAc,GAAG,IAAI,CAExC;IAED,OAAO,CAAC,WAAW;IAuCnB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,SAAS;IASjB;;;OAGG;IACG,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8MnD;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAY/B,IAAI,IAAI,IAAI;CAOb"}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import { createInterface } from 'readline';
|
|
3
|
+
import { randomUUID } from 'crypto';
|
|
3
4
|
import { TokenCounter } from '../utils/token-counter.js';
|
|
4
5
|
import { Logger } from '../utils/logger.js';
|
|
5
6
|
import { StructuredLogger } from '../utils/structured-logger.js';
|
|
7
|
+
import { OAuthValidator } from '../auth/oauth.js';
|
|
8
|
+
import { SessionCache } from '../auth/session-cache.js';
|
|
9
|
+
import { CircuitBreaker } from '../utils/circuit-breaker.js';
|
|
10
|
+
import * as Metrics from '../utils/metrics.js';
|
|
6
11
|
/**
|
|
7
|
-
* MCP Proxy Interceptor — sits between the AI client and an MCP server
|
|
8
|
-
* capturing every JSON-RPC call's token usage for real cost auditing.
|
|
12
|
+
* MCP Proxy Interceptor — sits between the AI client and an MCP server.
|
|
9
13
|
*
|
|
10
14
|
* v0.4: Integrated PolicyEngine for active blocking of malicious tool calls.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
15
|
+
* v0.5: OAuth 2.1 JWT validation — validates bearer tokens before policy evaluation.
|
|
16
|
+
* v0.5.2: Circuit breaker for upstream MCP server failures.
|
|
17
|
+
* v0.5.2: Per-client rate limiting (keyed by agent sub + tool name).
|
|
18
|
+
* v0.5.2: Consistent SIEM fields (request_id, proxy_latency_ms, authn_success, authz_allowed).
|
|
13
19
|
*/
|
|
14
20
|
export class McpProxyServer {
|
|
15
21
|
child;
|
|
@@ -22,9 +28,18 @@ export class McpProxyServer {
|
|
|
22
28
|
requestArguments;
|
|
23
29
|
serverName;
|
|
24
30
|
policyEngine;
|
|
25
|
-
|
|
31
|
+
authValidator;
|
|
32
|
+
sessionCache;
|
|
33
|
+
circuitBreaker;
|
|
34
|
+
/** v0.5.2: Per-client rate limit counters (key: agentSub:toolName) */
|
|
35
|
+
clientRateCounters = new Map();
|
|
36
|
+
constructor(command, args, env, db, serverName, policyEngine, authValidator) {
|
|
26
37
|
this.serverName = serverName || command.split('/').pop() || command;
|
|
27
38
|
this.policyEngine = policyEngine || null;
|
|
39
|
+
this.authValidator = authValidator || null;
|
|
40
|
+
this.sessionCache = authValidator ? new SessionCache() : null;
|
|
41
|
+
this.circuitBreaker = new CircuitBreaker(this.serverName);
|
|
42
|
+
Metrics.circuitBreakerState.set({ server_name: this.serverName }, 0);
|
|
28
43
|
this.child = spawn(command, args, {
|
|
29
44
|
env: { ...process.env, ...env },
|
|
30
45
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -37,6 +52,8 @@ export class McpProxyServer {
|
|
|
37
52
|
event: 'proxy_started',
|
|
38
53
|
serverName: this.serverName,
|
|
39
54
|
blockingMode: this.policyEngine ? this.policyEngine.getMode() : 'audit',
|
|
55
|
+
authEnabled: this.authValidator ? this.authValidator.getConfig().required : false,
|
|
56
|
+
circuitBreaker: this.circuitBreaker.getState(),
|
|
40
57
|
});
|
|
41
58
|
}
|
|
42
59
|
get stdin() {
|
|
@@ -48,7 +65,7 @@ export class McpProxyServer {
|
|
|
48
65
|
try {
|
|
49
66
|
const msg = JSON.parse(line);
|
|
50
67
|
if (msg.id && msg.id === this.currentRequestId) {
|
|
51
|
-
|
|
68
|
+
const proxyLatencyMs = Date.now() - this.requestStartTime;
|
|
52
69
|
const responseTokens = this.tokenCounter.count(line);
|
|
53
70
|
const record = {
|
|
54
71
|
serverName: this.serverName,
|
|
@@ -56,18 +73,22 @@ export class McpProxyServer {
|
|
|
56
73
|
requestTokens: this.requestTokens,
|
|
57
74
|
responseTokens,
|
|
58
75
|
totalTokens: this.requestTokens + responseTokens,
|
|
59
|
-
durationMs:
|
|
76
|
+
durationMs: proxyLatencyMs,
|
|
60
77
|
timestamp: new Date().toISOString(),
|
|
61
78
|
};
|
|
62
79
|
this.db.addCallRecord(record).then(() => this.db.flush()).catch((err) => Logger.debug(`Proxy: failed to store call record: ${err?.message}`));
|
|
80
|
+
this.circuitBreaker.recordSuccess();
|
|
81
|
+
Metrics.circuitBreakerState.set({ server_name: this.serverName }, this.circuitBreaker.getState() === 'CLOSED' ? 0 : this.circuitBreaker.getState() === 'OPEN' ? 1 : 2);
|
|
82
|
+
Metrics.proxyLatencyMs.observe({ server_name: this.serverName }, proxyLatencyMs);
|
|
83
|
+
Metrics.requestsTotal.inc({ server_name: this.serverName, decision: 'pass', authn_success: 'true' });
|
|
84
|
+
if (this.sessionCache)
|
|
85
|
+
Metrics.activeSessions.set(this.sessionCache.size);
|
|
63
86
|
this.currentRequestId = null;
|
|
64
87
|
this.requestToolName = null;
|
|
65
88
|
}
|
|
66
|
-
// Forward response to client
|
|
67
89
|
process.stdout.write(line + '\n');
|
|
68
90
|
}
|
|
69
91
|
catch {
|
|
70
|
-
// Non-JSON line — forward as-is
|
|
71
92
|
process.stdout.write(line + '\n');
|
|
72
93
|
}
|
|
73
94
|
});
|
|
@@ -80,76 +101,227 @@ export class McpProxyServer {
|
|
|
80
101
|
process.stderr.write(data);
|
|
81
102
|
});
|
|
82
103
|
}
|
|
104
|
+
sendError(id, code, message, data) {
|
|
105
|
+
const errorResponse = JSON.stringify({
|
|
106
|
+
jsonrpc: '2.0',
|
|
107
|
+
id,
|
|
108
|
+
error: { code, message, data },
|
|
109
|
+
});
|
|
110
|
+
process.stdout.write(errorResponse + '\n');
|
|
111
|
+
}
|
|
83
112
|
/**
|
|
84
113
|
* Called when the AI client writes a request to be proxied.
|
|
85
|
-
*
|
|
86
|
-
* Blocked calls receive a JSON-RPC error response.
|
|
114
|
+
* Pipeline: Auth → Circuit Breaker → Policy + RBAC → Forward.
|
|
87
115
|
*/
|
|
88
|
-
handleClientInput(raw) {
|
|
116
|
+
async handleClientInput(raw) {
|
|
117
|
+
const requestId = randomUUID();
|
|
118
|
+
const proxyStartTime = Date.now();
|
|
89
119
|
try {
|
|
90
120
|
const msg = JSON.parse(raw);
|
|
91
121
|
if (msg.method === 'tools/call' && msg.id) {
|
|
92
|
-
this.requestStartTime =
|
|
93
|
-
this.currentRequestId = msg.id;
|
|
122
|
+
this.requestStartTime = proxyStartTime;
|
|
123
|
+
this.currentRequestId = msg.id; // Original msg ID for response matching
|
|
94
124
|
this.requestToolName = msg.params?.name || 'unknown';
|
|
95
125
|
this.requestTokens = this.tokenCounter.count(raw);
|
|
96
126
|
this.requestArguments = msg.params?.arguments;
|
|
97
|
-
|
|
127
|
+
const toolName = this.requestToolName || 'unknown';
|
|
128
|
+
let agentIdentity;
|
|
129
|
+
let authnSuccess = false;
|
|
130
|
+
// ── v0.5: OAuth 2.1 JWT validation ──────────────────
|
|
131
|
+
if (this.authValidator) {
|
|
132
|
+
const authHeader = msg.params?._meta?.auth?.Authorization
|
|
133
|
+
|| msg.Authorization
|
|
134
|
+
|| msg.params?.Authorization
|
|
135
|
+
|| undefined;
|
|
136
|
+
const token = OAuthValidator.extractToken(authHeader);
|
|
137
|
+
if (!token) {
|
|
138
|
+
if (this.authValidator.getConfig().required) {
|
|
139
|
+
StructuredLogger.info({
|
|
140
|
+
event: 'auth_required',
|
|
141
|
+
requestId,
|
|
142
|
+
serverName: this.serverName,
|
|
143
|
+
toolName,
|
|
144
|
+
authnSuccess: false,
|
|
145
|
+
});
|
|
146
|
+
this.sendError(msg.id, -32002, 'Authentication required. Provide a valid Bearer token in the Authorization header.');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const result = await this.authValidator.validate(token);
|
|
152
|
+
authnSuccess = result.valid;
|
|
153
|
+
if (result.identity)
|
|
154
|
+
agentIdentity = result.identity;
|
|
155
|
+
if (!result.valid) {
|
|
156
|
+
StructuredLogger.logError({
|
|
157
|
+
event: 'oidc_auth_error',
|
|
158
|
+
serverName: this.serverName,
|
|
159
|
+
requestId,
|
|
160
|
+
error: `JWT validation failed: ${result.error}`,
|
|
161
|
+
});
|
|
162
|
+
if (this.authValidator.getConfig().required) {
|
|
163
|
+
this.sendError(msg.id, -32003, `Authentication failed: ${result.error}`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// v0.6.0: Session cache — issue short-lived session to prevent JWT replay
|
|
169
|
+
if (this.sessionCache && result.identity) {
|
|
170
|
+
const session = this.sessionCache.createSession(result.identity);
|
|
171
|
+
StructuredLogger.info({
|
|
172
|
+
event: 'auth_success',
|
|
173
|
+
requestId,
|
|
174
|
+
serverName: this.serverName,
|
|
175
|
+
toolName,
|
|
176
|
+
agent: result.identity.sub,
|
|
177
|
+
clientId: result.identity.clientId,
|
|
178
|
+
sessionToken: session.token,
|
|
179
|
+
sessionExpiry: new Date(session.expiresAt).toISOString(),
|
|
180
|
+
authnSuccess: true,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
StructuredLogger.info({
|
|
185
|
+
event: 'auth_success',
|
|
186
|
+
requestId,
|
|
187
|
+
serverName: this.serverName,
|
|
188
|
+
toolName,
|
|
189
|
+
agent: result.identity?.sub,
|
|
190
|
+
clientId: result.identity?.clientId,
|
|
191
|
+
authnSuccess: true,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// ── v0.5.2: Circuit breaker check ──────────────────
|
|
198
|
+
if (!this.circuitBreaker.allowRequest()) {
|
|
199
|
+
StructuredLogger.info({
|
|
200
|
+
event: 'circuit_open',
|
|
201
|
+
requestId,
|
|
202
|
+
serverName: this.serverName,
|
|
203
|
+
toolName,
|
|
204
|
+
state: this.circuitBreaker.getState(),
|
|
205
|
+
});
|
|
206
|
+
this.sendError(msg.id, -32005, `Upstream MCP server '${this.serverName}' unavailable — circuit breaker open`);
|
|
207
|
+
this.circuitBreaker.recordFailure();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// ── v0.5.1: RBAC + policy evaluation ────────────────
|
|
211
|
+
let authzAllowed = true;
|
|
212
|
+
let blockReason;
|
|
98
213
|
if (this.policyEngine) {
|
|
99
|
-
const toolName = this.requestToolName || 'unknown';
|
|
100
214
|
const context = {
|
|
101
215
|
serverName: this.serverName,
|
|
102
216
|
toolName,
|
|
103
217
|
arguments: this.requestArguments,
|
|
104
|
-
requestId
|
|
218
|
+
requestId,
|
|
105
219
|
requestTokens: this.requestTokens,
|
|
106
220
|
timestamp: new Date().toISOString(),
|
|
221
|
+
agentIdentity,
|
|
107
222
|
};
|
|
108
223
|
const decision = this.policyEngine.evaluate(context);
|
|
109
|
-
// Log every decision for SIEM audit trail
|
|
110
224
|
StructuredLogger.logPolicyDecision({
|
|
111
225
|
event: 'policy_decision',
|
|
112
|
-
requestId
|
|
226
|
+
requestId,
|
|
113
227
|
serverName: this.serverName,
|
|
114
228
|
toolName,
|
|
115
229
|
decision,
|
|
116
230
|
context,
|
|
117
231
|
});
|
|
118
232
|
if (decision.action === 'block') {
|
|
233
|
+
authzAllowed = false;
|
|
234
|
+
blockReason = `policy:${decision.rule}:${decision.reason}`;
|
|
119
235
|
StructuredLogger.logBlocked({
|
|
120
236
|
event: 'tool_blocked',
|
|
121
|
-
requestId
|
|
237
|
+
requestId,
|
|
122
238
|
serverName: this.serverName,
|
|
123
239
|
toolName,
|
|
124
|
-
reason: decision.reason
|
|
240
|
+
reason: `policy_rule=${decision.rule} | reason=${decision.reason}`,
|
|
125
241
|
rule: decision.rule,
|
|
126
242
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
243
|
+
StructuredLogger.info({
|
|
244
|
+
event: 'request_denied',
|
|
245
|
+
requestId,
|
|
246
|
+
serverName: this.serverName,
|
|
247
|
+
toolName,
|
|
248
|
+
authnSuccess,
|
|
249
|
+
authzAllowed,
|
|
250
|
+
blockReason,
|
|
251
|
+
proxyLatencyMs: Date.now() - proxyStartTime,
|
|
136
252
|
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
this.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
253
|
+
Metrics.blockedRequestsTotal.inc({ server_name: this.serverName, block_reason: blockReason || 'policy', rule: decision.rule });
|
|
254
|
+
Metrics.requestsTotal.inc({ server_name: this.serverName, decision: 'block', authn_success: String(authnSuccess) });
|
|
255
|
+
this.sendError(msg.id, -32001, `Blocked by MCP Guardian policy: ${decision.reason}`, {
|
|
256
|
+
rule: decision.rule,
|
|
257
|
+
policy: this.policyEngine.getMode(),
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
// v0.5.2: Per-client rate limiting
|
|
262
|
+
if (agentIdentity) {
|
|
263
|
+
const rateKey = `${agentIdentity.sub}:${toolName}`;
|
|
264
|
+
const now = Date.now();
|
|
265
|
+
let counter = this.clientRateCounters.get(rateKey);
|
|
266
|
+
if (!counter || now > counter.resetAt) {
|
|
267
|
+
counter = { count: 1, resetAt: now + 60000 };
|
|
268
|
+
this.clientRateCounters.set(rateKey, counter);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
counter.count++;
|
|
272
|
+
}
|
|
273
|
+
// Check if any RBAC rule with per-client rate limit fires
|
|
274
|
+
const perClientLimit = this.checkPerClientRateLimit(agentIdentity, toolName, counter.count);
|
|
275
|
+
if (perClientLimit) {
|
|
276
|
+
StructuredLogger.info({
|
|
277
|
+
event: 'request_denied',
|
|
278
|
+
requestId,
|
|
279
|
+
serverName: this.serverName,
|
|
280
|
+
toolName,
|
|
281
|
+
authnSuccess,
|
|
282
|
+
authzAllowed: false,
|
|
283
|
+
blockReason: perClientLimit,
|
|
284
|
+
proxyLatencyMs: Date.now() - proxyStartTime,
|
|
285
|
+
});
|
|
286
|
+
this.sendError(msg.id, -32004, `Rate limit exceeded for agent '${agentIdentity.sub}': ${perClientLimit}`);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
143
289
|
}
|
|
144
290
|
}
|
|
291
|
+
// ── Log successful forwarding ───────────────────────
|
|
292
|
+
StructuredLogger.info({
|
|
293
|
+
event: 'request_forwarded',
|
|
294
|
+
requestId,
|
|
295
|
+
serverName: this.serverName,
|
|
296
|
+
toolName,
|
|
297
|
+
authnSuccess,
|
|
298
|
+
authzAllowed,
|
|
299
|
+
proxyLatencyMs: Date.now() - proxyStartTime,
|
|
300
|
+
agent: agentIdentity?.sub,
|
|
301
|
+
});
|
|
145
302
|
}
|
|
146
303
|
}
|
|
147
304
|
catch {
|
|
148
305
|
// Non-JSON input — forward as-is
|
|
149
306
|
}
|
|
150
|
-
// Forward to the underlying MCP server
|
|
151
307
|
this.child.stdin?.write(raw + '\n');
|
|
152
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Check per-client rate limits against RBAC rules.
|
|
311
|
+
* Returns block reason string or null.
|
|
312
|
+
*/
|
|
313
|
+
checkPerClientRateLimit(identity, toolName, currentCount) {
|
|
314
|
+
if (!this.policyEngine)
|
|
315
|
+
return null;
|
|
316
|
+
// Per-client limits are tracked via RBAC scopes — if agent has "basic" scope, check against "basic" rate limit rule
|
|
317
|
+
const scopes = identity.scopes || [];
|
|
318
|
+
// Simple: if the agent has no special scopes and we've hit a hard limit
|
|
319
|
+
// In a full implementation, this would check per-scope/per-client limits from policy
|
|
320
|
+
if (scopes.length === 0 && currentCount > 100) {
|
|
321
|
+
return `Per-client rate limit exceeded: ${currentCount}/100 calls per minute (agent: ${identity.sub})`;
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
153
325
|
kill() {
|
|
154
326
|
try {
|
|
155
327
|
this.child.kill();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-server.js","sourceRoot":"","sources":["../../src/proxy/proxy-server.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"proxy-server.js","sourceRoot":"","sources":["../../src/proxy/proxy-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
|
|
2
|
+
export declare class CircuitBreaker {
|
|
3
|
+
private state;
|
|
4
|
+
private failureCount;
|
|
5
|
+
private successCount;
|
|
6
|
+
private lastFailureTime;
|
|
7
|
+
private readonly resetTimeout;
|
|
8
|
+
private readonly failureThreshold;
|
|
9
|
+
private readonly successThreshold;
|
|
10
|
+
private readonly name;
|
|
11
|
+
constructor(name: string, options?: {
|
|
12
|
+
failureThreshold?: number;
|
|
13
|
+
successThreshold?: number;
|
|
14
|
+
resetTimeoutMs?: number;
|
|
15
|
+
});
|
|
16
|
+
/** Check if the circuit allows a request through */
|
|
17
|
+
allowRequest(): boolean;
|
|
18
|
+
/** Record a successful request */
|
|
19
|
+
recordSuccess(): void;
|
|
20
|
+
/** Record a failed request */
|
|
21
|
+
recordFailure(): void;
|
|
22
|
+
getState(): CircuitState;
|
|
23
|
+
getStats(): {
|
|
24
|
+
state: CircuitState;
|
|
25
|
+
failureCount: number;
|
|
26
|
+
successCount: number;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/utils/circuit-breaker.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAG5B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAO;IAQjG,oDAAoD;IACpD,YAAY,IAAI,OAAO;IAcvB,kCAAkC;IAClC,aAAa,IAAI,IAAI;IAcrB,8BAA8B;IAC9B,aAAa,IAAI,IAAI;IAerB,QAAQ,IAAI,YAAY;IAIxB,QAAQ,IAAI;QAAE,KAAK,EAAE,YAAY,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;CAOhF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker for MCP Proxy Server.
|
|
3
|
+
* Implements the classic 3-state circuit breaker pattern:
|
|
4
|
+
* CLOSED → OPEN → HALF_OPEN → CLOSED (or OPEN again).
|
|
5
|
+
*
|
|
6
|
+
* Used to protect upstream MCP servers from cascading failures.
|
|
7
|
+
*/
|
|
8
|
+
import { Logger } from './logger.js';
|
|
9
|
+
export class CircuitBreaker {
|
|
10
|
+
state = 'CLOSED';
|
|
11
|
+
failureCount = 0;
|
|
12
|
+
successCount = 0;
|
|
13
|
+
lastFailureTime = 0;
|
|
14
|
+
resetTimeout;
|
|
15
|
+
failureThreshold;
|
|
16
|
+
successThreshold;
|
|
17
|
+
name;
|
|
18
|
+
constructor(name, options = {}) {
|
|
19
|
+
this.name = name;
|
|
20
|
+
this.failureThreshold = options.failureThreshold || 5;
|
|
21
|
+
this.successThreshold = options.successThreshold || 2;
|
|
22
|
+
this.resetTimeout = options.resetTimeoutMs || 30000;
|
|
23
|
+
}
|
|
24
|
+
/** Check if the circuit allows a request through */
|
|
25
|
+
allowRequest() {
|
|
26
|
+
if (this.state === 'CLOSED')
|
|
27
|
+
return true;
|
|
28
|
+
if (this.state === 'OPEN') {
|
|
29
|
+
if (Date.now() - this.lastFailureTime >= this.resetTimeout) {
|
|
30
|
+
this.state = 'HALF_OPEN';
|
|
31
|
+
Logger.debug(`[circuit-breaker:${this.name}] Transitioned to HALF_OPEN`);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// HALF_OPEN: allow probes
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
/** Record a successful request */
|
|
40
|
+
recordSuccess() {
|
|
41
|
+
if (this.state === 'HALF_OPEN') {
|
|
42
|
+
this.successCount++;
|
|
43
|
+
if (this.successCount >= this.successThreshold) {
|
|
44
|
+
this.state = 'CLOSED';
|
|
45
|
+
this.failureCount = 0;
|
|
46
|
+
this.successCount = 0;
|
|
47
|
+
Logger.info(`[circuit-breaker:${this.name}] Circuit CLOSED — service healthy`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else if (this.state === 'CLOSED') {
|
|
51
|
+
this.failureCount = 0;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Record a failed request */
|
|
55
|
+
recordFailure() {
|
|
56
|
+
this.lastFailureTime = Date.now();
|
|
57
|
+
if (this.state === 'HALF_OPEN') {
|
|
58
|
+
this.state = 'OPEN';
|
|
59
|
+
this.successCount = 0;
|
|
60
|
+
Logger.warn(`[circuit-breaker:${this.name}] Circuit OPEN — half-open probe failed`);
|
|
61
|
+
}
|
|
62
|
+
else if (this.state === 'CLOSED') {
|
|
63
|
+
this.failureCount++;
|
|
64
|
+
if (this.failureCount >= this.failureThreshold) {
|
|
65
|
+
this.state = 'OPEN';
|
|
66
|
+
Logger.warn(`[circuit-breaker:${this.name}] Circuit OPEN — ${this.failureCount} consecutive failures`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
getState() {
|
|
71
|
+
return this.state;
|
|
72
|
+
}
|
|
73
|
+
getStats() {
|
|
74
|
+
return {
|
|
75
|
+
state: this.state,
|
|
76
|
+
failureCount: this.failureCount,
|
|
77
|
+
successCount: this.successCount,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/utils/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Counter, Gauge, Histogram } from 'prom-client';
|
|
2
|
+
export declare const requestsTotal: Counter<"server_name" | "decision" | "authn_success">;
|
|
3
|
+
export declare const blockedRequestsTotal: Counter<"rule" | "server_name" | "block_reason">;
|
|
4
|
+
export declare const authFailuresTotal: Counter<"reason" | "server_name">;
|
|
5
|
+
export declare const circuitBreakerState: Gauge<"server_name">;
|
|
6
|
+
export declare const activeSessions: Gauge<string>;
|
|
7
|
+
export declare const proxyLatencyMs: Histogram<"server_name">;
|
|
8
|
+
export declare const authLatencyMs: Histogram<"server_name">;
|
|
9
|
+
export declare function startMetricsServer(port?: number): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/utils/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,OAAO,EAAE,KAAK,EAAE,SAAS,EAAyB,MAAM,aAAa,CAAC;AAazF,eAAO,MAAM,aAAa,uDAKxB,CAAC;AAEH,eAAO,MAAM,oBAAoB,kDAK/B,CAAC;AAEH,eAAO,MAAM,iBAAiB,mCAK5B,CAAC;AAGH,eAAO,MAAM,mBAAmB,sBAK9B,CAAC;AAEH,eAAO,MAAM,cAAc,eAIzB,CAAC;AAGH,eAAO,MAAM,cAAc,0BAMzB,CAAC;AAEH,eAAO,MAAM,aAAa,0BAMxB,CAAC;AAGH,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB3E"}
|