@mattral/collector 1.1.0 → 1.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/package.json +2 -2
- package/dist/error-message.d.ts +0 -28
- package/dist/error-message.d.ts.map +0 -1
- package/dist/error-message.js +0 -42
- package/dist/error-message.js.map +0 -1
- package/dist/health-checker.d.ts +0 -43
- package/dist/health-checker.d.ts.map +0 -1
- package/dist/health-checker.js +0 -93
- package/dist/health-checker.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +0 -1
- package/dist/json-rpc-framer.d.ts +0 -51
- package/dist/json-rpc-framer.d.ts.map +0 -1
- package/dist/json-rpc-framer.js +0 -46
- package/dist/json-rpc-framer.js.map +0 -1
- package/dist/output-payload.d.ts +0 -32
- package/dist/output-payload.d.ts.map +0 -1
- package/dist/output-payload.js +0 -54
- package/dist/output-payload.js.map +0 -1
- package/dist/proxy-collector.d.ts +0 -83
- package/dist/proxy-collector.d.ts.map +0 -1
- package/dist/proxy-collector.js +0 -214
- package/dist/proxy-collector.js.map +0 -1
- package/dist/transport-factory.d.ts +0 -11
- package/dist/transport-factory.d.ts.map +0 -1
- package/dist/transport-factory.js +0 -34
- package/dist/transport-factory.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mattral/collector",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Health checking and traffic-observing proxy collectors for MCP servers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@mattral/core": "^1.0.0",
|
|
48
48
|
"@mattral/storage": "^1.0.0",
|
|
49
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
49
|
+
"@modelcontextprotocol/sdk": "^1.32.2"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"typescript": "^5.7.3"
|
package/dist/error-message.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { JsonRpcMessage } from "./json-rpc-framer.js";
|
|
2
|
-
/**
|
|
3
|
-
* Extracts a human-readable error message from a raw MCP `tools/call`
|
|
4
|
-
* response, for both kinds of failure the protocol allows:
|
|
5
|
-
*
|
|
6
|
-
* - **Protocol-level failures**: the JSON-RPC response has an `error`
|
|
7
|
-
* object (e.g. a malformed request, a transport-level failure). Its
|
|
8
|
-
* `.message` is already the right text — nothing to unwrap.
|
|
9
|
-
* - **Tool-level failures**: the JSON-RPC response is a normal `result`
|
|
10
|
-
* with `isError: true`, and the actual, tool-specific error description
|
|
11
|
-
* lives in `result.content` as ordinary text blocks — this is the MCP
|
|
12
|
-
* spec's convention for a tool reporting its *own* failure without it
|
|
13
|
-
* being treated as a transport-level protocol error (this project's own
|
|
14
|
-
* reference server's `flaky` tool does exactly this).
|
|
15
|
-
*
|
|
16
|
-
* Before this function existed, every tool-level failure's real error
|
|
17
|
-
* text was discarded in favor of one generic placeholder string,
|
|
18
|
-
* regardless of what the tool actually said — the same underlying mistake
|
|
19
|
-
* as the pre-`extractOutputPayload` output-shape bug (see
|
|
20
|
-
* docs/decisions/0015-statistical-drift-detection.md): treating MCP's
|
|
21
|
-
* `content` array as opaque instead of reading what's actually inside it.
|
|
22
|
-
* See docs/decisions/0016-error-classification.md.
|
|
23
|
-
*
|
|
24
|
-
* Falls back to a generic placeholder only when there's truly no error
|
|
25
|
-
* object and no text content to describe the failure at all.
|
|
26
|
-
*/
|
|
27
|
-
export declare function extractErrorMessage(message: JsonRpcMessage): string;
|
|
28
|
-
//# sourceMappingURL=error-message.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error-message.d.ts","sourceRoot":"","sources":["../src/error-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAkBnE"}
|
package/dist/error-message.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts a human-readable error message from a raw MCP `tools/call`
|
|
3
|
-
* response, for both kinds of failure the protocol allows:
|
|
4
|
-
*
|
|
5
|
-
* - **Protocol-level failures**: the JSON-RPC response has an `error`
|
|
6
|
-
* object (e.g. a malformed request, a transport-level failure). Its
|
|
7
|
-
* `.message` is already the right text — nothing to unwrap.
|
|
8
|
-
* - **Tool-level failures**: the JSON-RPC response is a normal `result`
|
|
9
|
-
* with `isError: true`, and the actual, tool-specific error description
|
|
10
|
-
* lives in `result.content` as ordinary text blocks — this is the MCP
|
|
11
|
-
* spec's convention for a tool reporting its *own* failure without it
|
|
12
|
-
* being treated as a transport-level protocol error (this project's own
|
|
13
|
-
* reference server's `flaky` tool does exactly this).
|
|
14
|
-
*
|
|
15
|
-
* Before this function existed, every tool-level failure's real error
|
|
16
|
-
* text was discarded in favor of one generic placeholder string,
|
|
17
|
-
* regardless of what the tool actually said — the same underlying mistake
|
|
18
|
-
* as the pre-`extractOutputPayload` output-shape bug (see
|
|
19
|
-
* docs/decisions/0015-statistical-drift-detection.md): treating MCP's
|
|
20
|
-
* `content` array as opaque instead of reading what's actually inside it.
|
|
21
|
-
* See docs/decisions/0016-error-classification.md.
|
|
22
|
-
*
|
|
23
|
-
* Falls back to a generic placeholder only when there's truly no error
|
|
24
|
-
* object and no text content to describe the failure at all.
|
|
25
|
-
*/
|
|
26
|
-
export function extractErrorMessage(message) {
|
|
27
|
-
if (message.error?.message)
|
|
28
|
-
return message.error.message;
|
|
29
|
-
const content = message.result?.content;
|
|
30
|
-
if (Array.isArray(content)) {
|
|
31
|
-
const texts = content
|
|
32
|
-
.filter((block) => typeof block === "object" &&
|
|
33
|
-
block !== null &&
|
|
34
|
-
block.type === "text" &&
|
|
35
|
-
typeof block.text === "string")
|
|
36
|
-
.map((block) => block.text);
|
|
37
|
-
if (texts.length > 0)
|
|
38
|
-
return texts.join(" ");
|
|
39
|
-
}
|
|
40
|
-
return "Tool call returned isError: true, with no error text or content to describe why.";
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=error-message.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error-message.js","sourceRoot":"","sources":["../src/error-message.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAuB;IACzD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;IAEzD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;IACxC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,OAAO;aAClB,MAAM,CACL,CAAC,KAAK,EAA2C,EAAE,CACjD,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,KAAK,IAAI;YACb,KAA4B,CAAC,IAAI,KAAK,MAAM;YAC7C,OAAQ,KAA4B,CAAC,IAAI,KAAK,QAAQ,CACzD;aACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,kFAAkF,CAAC;AAC5F,CAAC"}
|
package/dist/health-checker.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type DriftEvent, type Server } from "@mattral/core";
|
|
2
|
-
import type { ReliabilityStore } from "@mattral/storage";
|
|
3
|
-
export interface HealthCheckResult {
|
|
4
|
-
serverId: string;
|
|
5
|
-
reachable: boolean;
|
|
6
|
-
latencyMs: number;
|
|
7
|
-
toolCount: number;
|
|
8
|
-
driftEvents: DriftEvent[];
|
|
9
|
-
error?: string;
|
|
10
|
-
}
|
|
11
|
-
export interface HealthCheckerOptions {
|
|
12
|
-
store: ReliabilityStore;
|
|
13
|
-
/** Client identity advertised to monitored servers during the MCP handshake. */
|
|
14
|
-
clientInfo?: {
|
|
15
|
-
name: string;
|
|
16
|
-
version: string;
|
|
17
|
-
};
|
|
18
|
-
/** Timeout for the initialize handshake and the tools/list round trip. Default 10s. */
|
|
19
|
-
timeoutMs?: number;
|
|
20
|
-
}
|
|
21
|
-
/** Reserved pseudo tool-name under which health-check round trips are recorded. */
|
|
22
|
-
export declare const HEALTH_CHECK_TOOL_NAME = "__health_check__";
|
|
23
|
-
/**
|
|
24
|
-
* Performs a single, side-effect-free health check against a server:
|
|
25
|
-
* connects, completes the MCP `initialize` handshake, calls `tools/list`,
|
|
26
|
-
* and disconnects.
|
|
27
|
-
*
|
|
28
|
-
* This deliberately never calls a tool. Per the project's operational
|
|
29
|
-
* principles (spec section 11 / docs/architecture.md#security), a
|
|
30
|
-
* monitoring tool must not exercise side effects on a server it doesn't
|
|
31
|
-
* have explicit consent to invoke. Observing *real* tool calls — the ones a
|
|
32
|
-
* live agent chose to make — is the job of {@link StdioProxyCollector}
|
|
33
|
-
* instead, where mcp-reliable never originates the call itself.
|
|
34
|
-
*/
|
|
35
|
-
export declare class HealthChecker {
|
|
36
|
-
private readonly store;
|
|
37
|
-
private readonly clientInfo;
|
|
38
|
-
private readonly timeoutMs;
|
|
39
|
-
constructor(options: HealthCheckerOptions);
|
|
40
|
-
check(server: Server): Promise<HealthCheckResult>;
|
|
41
|
-
private recordHealthCheckExecution;
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=health-checker.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"health-checker.d.ts","sourceRoot":"","sources":["../src/health-checker.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,UAAU,EAEf,KAAK,MAAM,EAEZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,gBAAgB,CAAC;IACxB,gFAAgF;IAChF,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoC;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,oBAAoB;IAMnC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAwDvD,OAAO,CAAC,0BAA0B;CAmBnC"}
|
package/dist/health-checker.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
-
import { computeSchemaFingerprint, detectSchemaDrift } from "@mattral/core";
|
|
4
|
-
import { createClientTransport } from "./transport-factory.js";
|
|
5
|
-
/** Reserved pseudo tool-name under which health-check round trips are recorded. */
|
|
6
|
-
export const HEALTH_CHECK_TOOL_NAME = "__health_check__";
|
|
7
|
-
/**
|
|
8
|
-
* Performs a single, side-effect-free health check against a server:
|
|
9
|
-
* connects, completes the MCP `initialize` handshake, calls `tools/list`,
|
|
10
|
-
* and disconnects.
|
|
11
|
-
*
|
|
12
|
-
* This deliberately never calls a tool. Per the project's operational
|
|
13
|
-
* principles (spec section 11 / docs/architecture.md#security), a
|
|
14
|
-
* monitoring tool must not exercise side effects on a server it doesn't
|
|
15
|
-
* have explicit consent to invoke. Observing *real* tool calls — the ones a
|
|
16
|
-
* live agent chose to make — is the job of {@link StdioProxyCollector}
|
|
17
|
-
* instead, where mcp-reliable never originates the call itself.
|
|
18
|
-
*/
|
|
19
|
-
export class HealthChecker {
|
|
20
|
-
store;
|
|
21
|
-
clientInfo;
|
|
22
|
-
timeoutMs;
|
|
23
|
-
constructor(options) {
|
|
24
|
-
this.store = options.store;
|
|
25
|
-
this.clientInfo = options.clientInfo ?? { name: "mcp-reliable", version: "1.0.0" };
|
|
26
|
-
this.timeoutMs = options.timeoutMs ?? 10_000;
|
|
27
|
-
}
|
|
28
|
-
async check(server) {
|
|
29
|
-
const start = performance.now();
|
|
30
|
-
const now = Date.now();
|
|
31
|
-
const client = new Client(this.clientInfo, { capabilities: {} });
|
|
32
|
-
const transport = createClientTransport(server.transport);
|
|
33
|
-
const driftEvents = [];
|
|
34
|
-
try {
|
|
35
|
-
await client.connect(transport, { timeout: this.timeoutMs });
|
|
36
|
-
const { tools } = await client.listTools(undefined, { timeout: this.timeoutMs });
|
|
37
|
-
const latencyMs = performance.now() - start;
|
|
38
|
-
for (const tool of tools) {
|
|
39
|
-
const fingerprint = computeSchemaFingerprint(tool.inputSchema);
|
|
40
|
-
const existing = this.store.getTool(server.id, tool.name);
|
|
41
|
-
const drift = detectSchemaDrift({
|
|
42
|
-
serverId: server.id,
|
|
43
|
-
toolName: tool.name,
|
|
44
|
-
previousFingerprint: existing?.schemaFingerprint,
|
|
45
|
-
currentFingerprint: fingerprint,
|
|
46
|
-
now
|
|
47
|
-
});
|
|
48
|
-
if (drift) {
|
|
49
|
-
this.store.recordDriftEvent(drift);
|
|
50
|
-
driftEvents.push(drift);
|
|
51
|
-
}
|
|
52
|
-
const toolRecord = {
|
|
53
|
-
id: existing?.id ?? randomUUID(),
|
|
54
|
-
serverId: server.id,
|
|
55
|
-
name: tool.name,
|
|
56
|
-
description: tool.description,
|
|
57
|
-
schemaFingerprint: fingerprint,
|
|
58
|
-
inputSchema: tool.inputSchema,
|
|
59
|
-
firstSeenAt: existing?.firstSeenAt ?? now,
|
|
60
|
-
lastSeenAt: now
|
|
61
|
-
};
|
|
62
|
-
this.store.upsertTool(toolRecord);
|
|
63
|
-
}
|
|
64
|
-
this.recordHealthCheckExecution(server.id, now, latencyMs, true);
|
|
65
|
-
return { serverId: server.id, reachable: true, latencyMs, toolCount: tools.length, driftEvents };
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
const latencyMs = performance.now() - start;
|
|
69
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
70
|
-
this.recordHealthCheckExecution(server.id, now, latencyMs, false, message);
|
|
71
|
-
return { serverId: server.id, reachable: false, latencyMs, toolCount: 0, driftEvents, error: message };
|
|
72
|
-
}
|
|
73
|
-
finally {
|
|
74
|
-
await client.close().catch(() => {
|
|
75
|
-
// Best-effort: the connection may already be dead if the check failed.
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
recordHealthCheckExecution(serverId, startedAt, durationMs, success, errorMessage) {
|
|
80
|
-
const execution = {
|
|
81
|
-
id: randomUUID(),
|
|
82
|
-
serverId,
|
|
83
|
-
toolName: HEALTH_CHECK_TOOL_NAME,
|
|
84
|
-
startedAt,
|
|
85
|
-
durationMs,
|
|
86
|
-
success,
|
|
87
|
-
errorMessage,
|
|
88
|
-
source: "health-check"
|
|
89
|
-
};
|
|
90
|
-
this.store.recordExecution(execution);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
//# sourceMappingURL=health-checker.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"health-checker.js","sourceRoot":"","sources":["../src/health-checker.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
DELETED
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
|
package/dist/index.js
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A minimal JSON-RPC 2.0 message shape, loosely typed on purpose: the proxy
|
|
3
|
-
* only needs to inspect `id`, `method`, `result`, and `error` — it forwards
|
|
4
|
-
* every other field verbatim without validating them against the full MCP
|
|
5
|
-
* type definitions (that validation is the target server's job).
|
|
6
|
-
*/
|
|
7
|
-
export interface JsonRpcMessage {
|
|
8
|
-
jsonrpc?: string;
|
|
9
|
-
id?: string | number;
|
|
10
|
-
method?: string;
|
|
11
|
-
params?: {
|
|
12
|
-
name?: string;
|
|
13
|
-
arguments?: unknown;
|
|
14
|
-
[key: string]: unknown;
|
|
15
|
-
};
|
|
16
|
-
result?: {
|
|
17
|
-
isError?: boolean;
|
|
18
|
-
content?: unknown;
|
|
19
|
-
[key: string]: unknown;
|
|
20
|
-
};
|
|
21
|
-
error?: {
|
|
22
|
-
code: number;
|
|
23
|
-
message: string;
|
|
24
|
-
[key: string]: unknown;
|
|
25
|
-
};
|
|
26
|
-
[key: string]: unknown;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The MCP stdio transport frames JSON-RPC messages as one JSON object per
|
|
30
|
-
* line (newline-delimited), per the MCP specification. This class buffers
|
|
31
|
-
* partial reads from a stream and yields fully-parsed messages as they
|
|
32
|
-
* become available — the same approach the official SDK's internal
|
|
33
|
-
* ReadBuffer uses, reimplemented here so the proxy collector can stay a
|
|
34
|
-
* thin, dependency-light pass-through rather than depending on SDK
|
|
35
|
-
* internals that aren't part of its public API.
|
|
36
|
-
*
|
|
37
|
-
* Malformed lines are surfaced via the `onParseError` callback rather than
|
|
38
|
-
* thrown, since a single bad line should not take down the whole proxy —
|
|
39
|
-
* it is forwarded to the child/downstream as raw bytes regardless (the
|
|
40
|
-
* proxy's job is to observe traffic, not to enforce protocol correctness).
|
|
41
|
-
*/
|
|
42
|
-
export declare class JsonRpcFramer {
|
|
43
|
-
private readonly onMessage;
|
|
44
|
-
private readonly onParseError?;
|
|
45
|
-
private buffer;
|
|
46
|
-
constructor(onMessage: (message: JsonRpcMessage, raw: string) => void, onParseError?: ((raw: string, error: unknown) => void) | undefined);
|
|
47
|
-
push(chunk: Buffer | string): void;
|
|
48
|
-
}
|
|
49
|
-
/** Serializes a JSON-RPC message back to the newline-delimited wire format. */
|
|
50
|
-
export declare function serializeJsonRpc(message: JsonRpcMessage): string;
|
|
51
|
-
//# sourceMappingURL=json-rpc-framer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"json-rpc-framer.d.ts","sourceRoot":"","sources":["../src/json-rpc-framer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACxE,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC1E,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAClE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAa;IAItB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IAJhC,OAAO,CAAC,MAAM,CAAM;gBAGD,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,EACzD,YAAY,CAAC,GAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,aAAA;IAGvE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAgBnC;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAEhE"}
|
package/dist/json-rpc-framer.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The MCP stdio transport frames JSON-RPC messages as one JSON object per
|
|
3
|
-
* line (newline-delimited), per the MCP specification. This class buffers
|
|
4
|
-
* partial reads from a stream and yields fully-parsed messages as they
|
|
5
|
-
* become available — the same approach the official SDK's internal
|
|
6
|
-
* ReadBuffer uses, reimplemented here so the proxy collector can stay a
|
|
7
|
-
* thin, dependency-light pass-through rather than depending on SDK
|
|
8
|
-
* internals that aren't part of its public API.
|
|
9
|
-
*
|
|
10
|
-
* Malformed lines are surfaced via the `onParseError` callback rather than
|
|
11
|
-
* thrown, since a single bad line should not take down the whole proxy —
|
|
12
|
-
* it is forwarded to the child/downstream as raw bytes regardless (the
|
|
13
|
-
* proxy's job is to observe traffic, not to enforce protocol correctness).
|
|
14
|
-
*/
|
|
15
|
-
export class JsonRpcFramer {
|
|
16
|
-
onMessage;
|
|
17
|
-
onParseError;
|
|
18
|
-
buffer = "";
|
|
19
|
-
constructor(onMessage, onParseError) {
|
|
20
|
-
this.onMessage = onMessage;
|
|
21
|
-
this.onParseError = onParseError;
|
|
22
|
-
}
|
|
23
|
-
push(chunk) {
|
|
24
|
-
this.buffer += chunk.toString("utf8");
|
|
25
|
-
let newlineIndex;
|
|
26
|
-
while ((newlineIndex = this.buffer.indexOf("\n")) !== -1) {
|
|
27
|
-
const line = this.buffer.slice(0, newlineIndex);
|
|
28
|
-
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
29
|
-
const trimmed = line.trim();
|
|
30
|
-
if (trimmed.length === 0)
|
|
31
|
-
continue;
|
|
32
|
-
try {
|
|
33
|
-
const message = JSON.parse(trimmed);
|
|
34
|
-
this.onMessage(message, line);
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
this.onParseError?.(line, error);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/** Serializes a JSON-RPC message back to the newline-delimited wire format. */
|
|
43
|
-
export function serializeJsonRpc(message) {
|
|
44
|
-
return JSON.stringify(message) + "\n";
|
|
45
|
-
}
|
|
46
|
-
//# sourceMappingURL=json-rpc-framer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"json-rpc-framer.js","sourceRoot":"","sources":["../src/json-rpc-framer.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,aAAa;IAIL;IACA;IAJX,MAAM,GAAG,EAAE,CAAC;IAEpB,YACmB,SAAyD,EACzD,YAAoD;QADpD,cAAS,GAAT,SAAS,CAAgD;QACzD,iBAAY,GAAZ,YAAY,CAAwC;IACpE,CAAC;IAEJ,IAAI,CAAC,KAAsB;QACzB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,YAAoB,CAAC;QACzB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACzD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACnC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAmB,CAAC;gBACtD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAChC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,OAAuB;IACtD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;AACxC,CAAC"}
|
package/dist/output-payload.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts the "meaningful" structured payload from a raw MCP
|
|
3
|
-
* `tools/call` result, for output-shape drift detection to actually be
|
|
4
|
-
* useful against it.
|
|
5
|
-
*
|
|
6
|
-
* Naively hashing `result.content` itself is close to useless for the
|
|
7
|
-
* extremely common case where a tool returns its real structured data as
|
|
8
|
-
* JSON text inside a single content block — `content: [{ type: "text",
|
|
9
|
-
* text: "{...}" }]` has the exact same `[{type, text}]` shape whether the
|
|
10
|
-
* embedded JSON is `{"message": "..."}` or `{"echoed": "...", "respondedAt":
|
|
11
|
-
* "..."}`. A structural hash of the wrapper alone would never notice the
|
|
12
|
-
* tool's actual output changed underneath it — this is exactly what this
|
|
13
|
-
* project's own reference server does (see
|
|
14
|
-
* `examples/reference-mcp-server/src/index.ts`), and what a great many
|
|
15
|
-
* real-world MCP tools do too, for any tool that doesn't declare a formal
|
|
16
|
-
* `outputSchema`. This function unwraps that, so what gets fingerprinted
|
|
17
|
-
* is the real payload, not MCP's transport-level envelope around it.
|
|
18
|
-
*
|
|
19
|
-
* Precedence:
|
|
20
|
-
* 1. `structuredContent`, if the tool declared an output schema and the
|
|
21
|
-
* SDK populated it — already a real object, nothing to unwrap.
|
|
22
|
-
* 2. A single text content block that parses as JSON — the common
|
|
23
|
-
* "structured data serialized as text" convention.
|
|
24
|
-
* 3. Otherwise, the raw `content` array as-is — still useful: catches a
|
|
25
|
-
* tool switching between text/image/resource blocks, or adding/
|
|
26
|
-
* removing blocks, even without parseable JSON inside them.
|
|
27
|
-
* 4. If there's no `content` or `structuredContent` at all, the raw
|
|
28
|
-
* result object, matching how this was computed before this function
|
|
29
|
-
* existed (see docs/decisions/0015-statistical-drift-detection.md).
|
|
30
|
-
*/
|
|
31
|
-
export declare function extractOutputPayload(result: unknown): unknown;
|
|
32
|
-
//# sourceMappingURL=output-payload.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"output-payload.d.ts","sourceRoot":"","sources":["../src/output-payload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAsB7D"}
|
package/dist/output-payload.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts the "meaningful" structured payload from a raw MCP
|
|
3
|
-
* `tools/call` result, for output-shape drift detection to actually be
|
|
4
|
-
* useful against it.
|
|
5
|
-
*
|
|
6
|
-
* Naively hashing `result.content` itself is close to useless for the
|
|
7
|
-
* extremely common case where a tool returns its real structured data as
|
|
8
|
-
* JSON text inside a single content block — `content: [{ type: "text",
|
|
9
|
-
* text: "{...}" }]` has the exact same `[{type, text}]` shape whether the
|
|
10
|
-
* embedded JSON is `{"message": "..."}` or `{"echoed": "...", "respondedAt":
|
|
11
|
-
* "..."}`. A structural hash of the wrapper alone would never notice the
|
|
12
|
-
* tool's actual output changed underneath it — this is exactly what this
|
|
13
|
-
* project's own reference server does (see
|
|
14
|
-
* `examples/reference-mcp-server/src/index.ts`), and what a great many
|
|
15
|
-
* real-world MCP tools do too, for any tool that doesn't declare a formal
|
|
16
|
-
* `outputSchema`. This function unwraps that, so what gets fingerprinted
|
|
17
|
-
* is the real payload, not MCP's transport-level envelope around it.
|
|
18
|
-
*
|
|
19
|
-
* Precedence:
|
|
20
|
-
* 1. `structuredContent`, if the tool declared an output schema and the
|
|
21
|
-
* SDK populated it — already a real object, nothing to unwrap.
|
|
22
|
-
* 2. A single text content block that parses as JSON — the common
|
|
23
|
-
* "structured data serialized as text" convention.
|
|
24
|
-
* 3. Otherwise, the raw `content` array as-is — still useful: catches a
|
|
25
|
-
* tool switching between text/image/resource blocks, or adding/
|
|
26
|
-
* removing blocks, even without parseable JSON inside them.
|
|
27
|
-
* 4. If there's no `content` or `structuredContent` at all, the raw
|
|
28
|
-
* result object, matching how this was computed before this function
|
|
29
|
-
* existed (see docs/decisions/0015-statistical-drift-detection.md).
|
|
30
|
-
*/
|
|
31
|
-
export function extractOutputPayload(result) {
|
|
32
|
-
if (result === null || typeof result !== "object")
|
|
33
|
-
return result;
|
|
34
|
-
const r = result;
|
|
35
|
-
if (r.structuredContent !== undefined)
|
|
36
|
-
return r.structuredContent;
|
|
37
|
-
if (Array.isArray(r.content)) {
|
|
38
|
-
if (r.content.length === 1) {
|
|
39
|
-
const block = r.content[0];
|
|
40
|
-
if (block?.type === "text" && typeof block.text === "string") {
|
|
41
|
-
try {
|
|
42
|
-
return JSON.parse(block.text);
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// Not JSON - a genuine plain-text result. Fall through to
|
|
46
|
-
// hashing the content array itself.
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return r.content;
|
|
51
|
-
}
|
|
52
|
-
return result;
|
|
53
|
-
}
|
|
54
|
-
//# sourceMappingURL=output-payload.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"output-payload.js","sourceRoot":"","sources":["../src/output-payload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAe;IAClD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IACjE,MAAM,CAAC,GAAG,MAA4D,CAAC;IAEvE,IAAI,CAAC,CAAC,iBAAiB,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC,iBAAiB,CAAC;IAElE,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAqC,CAAC;YAC/D,IAAI,KAAK,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7D,IAAI,CAAC;oBACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAChC,CAAC;gBAAC,MAAM,CAAC;oBACP,0DAA0D;oBAC1D,oCAAoC;gBACtC,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,CAAC,CAAC,OAAO,CAAC;IACnB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { type DriftEvent, type Execution } from "@mattral/core";
|
|
2
|
-
import type { ReliabilityStore } from "@mattral/storage";
|
|
3
|
-
export interface StdioProxyCollectorOptions {
|
|
4
|
-
serverId: string;
|
|
5
|
-
store: ReliabilityStore;
|
|
6
|
-
command: string;
|
|
7
|
-
args?: string[];
|
|
8
|
-
env?: Record<string, string>;
|
|
9
|
-
cwd?: string;
|
|
10
|
-
/** Called for every recorded execution, in addition to persisting it to the store. */
|
|
11
|
-
onExecution?: (execution: Execution) => void;
|
|
12
|
-
/** Called whenever a drift event is detected and recorded. */
|
|
13
|
-
onDrift?: (event: DriftEvent) => void;
|
|
14
|
-
/** Stream carrying traffic FROM the real client (defaults to this process's stdin). */
|
|
15
|
-
input?: NodeJS.ReadableStream;
|
|
16
|
-
/** Stream carrying traffic TO the real client (defaults to this process's stdout). */
|
|
17
|
-
output?: NodeJS.WritableStream;
|
|
18
|
-
/**
|
|
19
|
-
* How many executions of the *same tool* to accumulate between
|
|
20
|
-
* window-based drift checks (output-shape and statistical/latency).
|
|
21
|
-
* Unlike schema drift (an O(1) fingerprint compare, checked on every
|
|
22
|
-
* `tools/list`), these compare whole windows of stored executions —
|
|
23
|
-
* running that on literally every single call would mean a store query
|
|
24
|
-
* plus a KS test per call on hot paths. Default 10: frequent enough that
|
|
25
|
-
* drift is caught within a handful of seconds of typical traffic, cheap
|
|
26
|
-
* enough not to matter. See docs/decisions/0015-statistical-drift-detection.md.
|
|
27
|
-
*/
|
|
28
|
-
driftCheckIntervalCalls?: number;
|
|
29
|
-
/**
|
|
30
|
-
* Size of each of the two (baseline, recent) windows compared for
|
|
31
|
-
* window-based drift. Default 20, matching `detectLatencyDrift`'s own
|
|
32
|
-
* default `minSampleSize` — there's no point fetching a bigger window
|
|
33
|
-
* than the detector needs.
|
|
34
|
-
*/
|
|
35
|
-
driftWindowSize?: number;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* A transparent stdio proxy that sits between an MCP client (an agent) and
|
|
39
|
-
* a real MCP server: it spawns the target server as a child process and
|
|
40
|
-
* forwards every JSON-RPC message between the client and the child
|
|
41
|
-
* unmodified, while recording an `Execution` for every `tools/call`
|
|
42
|
-
* request/response pair it observes, and a `Tool` (with schema drift
|
|
43
|
-
* detection) for every `tools/list` response.
|
|
44
|
-
*
|
|
45
|
-
* Because it only ever forwards traffic a real client originated, it
|
|
46
|
-
* requires no special consent model of its own for the calls it observes —
|
|
47
|
-
* see docs/architecture.md#collection-model. From the client's point of
|
|
48
|
-
* view, this proxy *is* the MCP server: point your agent's server command
|
|
49
|
-
* at `mcp-reliable proxy <server-id> -- <original command>` instead of the
|
|
50
|
-
* original command directly.
|
|
51
|
-
*/
|
|
52
|
-
export declare class StdioProxyCollector {
|
|
53
|
-
private readonly options;
|
|
54
|
-
private readonly driftCheckIntervalCalls;
|
|
55
|
-
private readonly driftWindowSize;
|
|
56
|
-
private child;
|
|
57
|
-
private readonly pendingCalls;
|
|
58
|
-
private readonly pendingListRequests;
|
|
59
|
-
private readonly callCountByTool;
|
|
60
|
-
constructor(options: StdioProxyCollectorOptions);
|
|
61
|
-
/**
|
|
62
|
-
* Starts the child process and begins forwarding traffic. Resolves with
|
|
63
|
-
* the child's exit code once it terminates, or rejects if it could not
|
|
64
|
-
* be started at all (e.g. command not found).
|
|
65
|
-
*/
|
|
66
|
-
start(): Promise<number | null>;
|
|
67
|
-
stop(): Promise<void>;
|
|
68
|
-
private handleDownstreamMessage;
|
|
69
|
-
private handleUpstreamMessage;
|
|
70
|
-
private recordCallExecution;
|
|
71
|
-
private recordDrift;
|
|
72
|
-
/**
|
|
73
|
-
* Every `driftCheckIntervalCalls` calls to a given tool, pulls its last
|
|
74
|
-
* two `driftWindowSize`-execution windows and runs the window-based
|
|
75
|
-
* drift detectors (output-shape, latency) against them. Schema drift is
|
|
76
|
-
* handled separately in `recordToolsListResult` — it's a per-fingerprint
|
|
77
|
-
* compare, not a window comparison, so it runs on every `tools/list`
|
|
78
|
-
* instead of being throttled here.
|
|
79
|
-
*/
|
|
80
|
-
private maybeCheckWindowDrift;
|
|
81
|
-
private recordToolsListResult;
|
|
82
|
-
}
|
|
83
|
-
//# sourceMappingURL=proxy-collector.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-collector.d.ts","sourceRoot":"","sources":["../src/proxy-collector.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,UAAU,EACf,KAAK,SAAS,EAEf,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAKzD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,gBAAgB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IAC7C,8DAA8D;IAC9D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B;;;;;;;;;OASG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAoBD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA2C;IACxE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA8B;IAClE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;gBAEjD,OAAO,EAAE,0BAA0B;IAM/C;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA+CzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,mBAAmB;IA4B3B,OAAO,CAAC,WAAW;IAKnB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAsC7B,OAAO,CAAC,qBAAqB;CAgC9B"}
|
package/dist/proxy-collector.js
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { computeOutputSignature, computeSchemaFingerprint, detectLatencyDrift, detectOutputShapeDrift, detectSchemaDrift } from "@mattral/core";
|
|
4
|
-
import { JsonRpcFramer } from "./json-rpc-framer.js";
|
|
5
|
-
import { extractErrorMessage } from "./error-message.js";
|
|
6
|
-
import { extractOutputPayload } from "./output-payload.js";
|
|
7
|
-
function isToolsListResult(value) {
|
|
8
|
-
return (typeof value === "object" &&
|
|
9
|
-
value !== null &&
|
|
10
|
-
Array.isArray(value.tools));
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* A transparent stdio proxy that sits between an MCP client (an agent) and
|
|
14
|
-
* a real MCP server: it spawns the target server as a child process and
|
|
15
|
-
* forwards every JSON-RPC message between the client and the child
|
|
16
|
-
* unmodified, while recording an `Execution` for every `tools/call`
|
|
17
|
-
* request/response pair it observes, and a `Tool` (with schema drift
|
|
18
|
-
* detection) for every `tools/list` response.
|
|
19
|
-
*
|
|
20
|
-
* Because it only ever forwards traffic a real client originated, it
|
|
21
|
-
* requires no special consent model of its own for the calls it observes —
|
|
22
|
-
* see docs/architecture.md#collection-model. From the client's point of
|
|
23
|
-
* view, this proxy *is* the MCP server: point your agent's server command
|
|
24
|
-
* at `mcp-reliable proxy <server-id> -- <original command>` instead of the
|
|
25
|
-
* original command directly.
|
|
26
|
-
*/
|
|
27
|
-
export class StdioProxyCollector {
|
|
28
|
-
options;
|
|
29
|
-
driftCheckIntervalCalls;
|
|
30
|
-
driftWindowSize;
|
|
31
|
-
child;
|
|
32
|
-
pendingCalls = new Map();
|
|
33
|
-
pendingListRequests = new Set();
|
|
34
|
-
callCountByTool = new Map();
|
|
35
|
-
constructor(options) {
|
|
36
|
-
this.options = options;
|
|
37
|
-
this.driftCheckIntervalCalls = options.driftCheckIntervalCalls ?? 10;
|
|
38
|
-
this.driftWindowSize = options.driftWindowSize ?? 20;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Starts the child process and begins forwarding traffic. Resolves with
|
|
42
|
-
* the child's exit code once it terminates, or rejects if it could not
|
|
43
|
-
* be started at all (e.g. command not found).
|
|
44
|
-
*/
|
|
45
|
-
start() {
|
|
46
|
-
const { command, args = [], env, cwd } = this.options;
|
|
47
|
-
return new Promise((resolve, reject) => {
|
|
48
|
-
const child = spawn(command, args, {
|
|
49
|
-
env: env ? { ...process.env, ...env } : process.env,
|
|
50
|
-
cwd,
|
|
51
|
-
stdio: ["pipe", "pipe", "inherit"]
|
|
52
|
-
});
|
|
53
|
-
this.child = child;
|
|
54
|
-
const childStdin = child.stdin;
|
|
55
|
-
const childStdout = child.stdout;
|
|
56
|
-
if (!childStdin || !childStdout) {
|
|
57
|
-
reject(new Error("Failed to open stdio pipes for the child MCP server process"));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const input = this.options.input ?? process.stdin;
|
|
61
|
-
const output = this.options.output ?? process.stdout;
|
|
62
|
-
// Downstream: messages FROM the client TO the child (requests, notifications).
|
|
63
|
-
const downstreamFramer = new JsonRpcFramer((message, raw) => {
|
|
64
|
-
this.handleDownstreamMessage(message);
|
|
65
|
-
childStdin.write(raw + "\n");
|
|
66
|
-
}, (raw) => childStdin.write(raw + "\n") // forward unparseable lines verbatim; never drop traffic
|
|
67
|
-
);
|
|
68
|
-
input.on("data", (chunk) => downstreamFramer.push(chunk));
|
|
69
|
-
input.on("end", () => child.kill());
|
|
70
|
-
// Upstream: messages FROM the child TO the client (responses, notifications).
|
|
71
|
-
const upstreamFramer = new JsonRpcFramer((message, raw) => {
|
|
72
|
-
this.handleUpstreamMessage(message);
|
|
73
|
-
output.write(raw + "\n");
|
|
74
|
-
}, (raw) => output.write(raw + "\n"));
|
|
75
|
-
childStdout.on("data", (chunk) => upstreamFramer.push(chunk));
|
|
76
|
-
child.on("error", (error) => reject(error));
|
|
77
|
-
child.on("exit", (code) => resolve(code));
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
async stop() {
|
|
81
|
-
this.child?.kill();
|
|
82
|
-
}
|
|
83
|
-
handleDownstreamMessage(message) {
|
|
84
|
-
if (message.id === undefined)
|
|
85
|
-
return;
|
|
86
|
-
if (message.method === "tools/call") {
|
|
87
|
-
const toolName = message.params?.name ?? "unknown";
|
|
88
|
-
const inputBytes = Buffer.byteLength(JSON.stringify(message.params?.arguments ?? {}), "utf8");
|
|
89
|
-
this.pendingCalls.set(message.id, { toolName, startedAt: Date.now(), inputBytes });
|
|
90
|
-
}
|
|
91
|
-
else if (message.method === "tools/list") {
|
|
92
|
-
this.pendingListRequests.add(message.id);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
handleUpstreamMessage(message) {
|
|
96
|
-
if (message.id === undefined)
|
|
97
|
-
return;
|
|
98
|
-
const pendingCall = this.pendingCalls.get(message.id);
|
|
99
|
-
if (pendingCall) {
|
|
100
|
-
this.pendingCalls.delete(message.id);
|
|
101
|
-
this.recordCallExecution(pendingCall, message);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
if (this.pendingListRequests.has(message.id)) {
|
|
105
|
-
this.pendingListRequests.delete(message.id);
|
|
106
|
-
if (isToolsListResult(message.result)) {
|
|
107
|
-
this.recordToolsListResult(message.result);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
recordCallExecution(pending, message) {
|
|
112
|
-
const now = Date.now();
|
|
113
|
-
const durationMs = now - pending.startedAt;
|
|
114
|
-
const isError = Boolean(message.error) || Boolean(message.result?.isError);
|
|
115
|
-
const outputBytes = message.result !== undefined ? Buffer.byteLength(JSON.stringify(message.result), "utf8") : undefined;
|
|
116
|
-
const outputSignature = !isError && message.result !== undefined ? computeOutputSignature(extractOutputPayload(message.result)) : undefined;
|
|
117
|
-
const execution = {
|
|
118
|
-
id: randomUUID(),
|
|
119
|
-
serverId: this.options.serverId,
|
|
120
|
-
toolName: pending.toolName,
|
|
121
|
-
startedAt: pending.startedAt,
|
|
122
|
-
durationMs,
|
|
123
|
-
success: !isError,
|
|
124
|
-
errorMessage: isError ? extractErrorMessage(message) : undefined,
|
|
125
|
-
outputSignature,
|
|
126
|
-
inputBytes: pending.inputBytes,
|
|
127
|
-
outputBytes,
|
|
128
|
-
source: "proxy"
|
|
129
|
-
};
|
|
130
|
-
this.options.store.recordExecution(execution);
|
|
131
|
-
this.options.onExecution?.(execution);
|
|
132
|
-
this.maybeCheckWindowDrift(pending.toolName, now);
|
|
133
|
-
}
|
|
134
|
-
recordDrift(drift) {
|
|
135
|
-
this.options.store.recordDriftEvent(drift);
|
|
136
|
-
this.options.onDrift?.(drift);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Every `driftCheckIntervalCalls` calls to a given tool, pulls its last
|
|
140
|
-
* two `driftWindowSize`-execution windows and runs the window-based
|
|
141
|
-
* drift detectors (output-shape, latency) against them. Schema drift is
|
|
142
|
-
* handled separately in `recordToolsListResult` — it's a per-fingerprint
|
|
143
|
-
* compare, not a window comparison, so it runs on every `tools/list`
|
|
144
|
-
* instead of being throttled here.
|
|
145
|
-
*/
|
|
146
|
-
maybeCheckWindowDrift(toolName, now) {
|
|
147
|
-
const count = (this.callCountByTool.get(toolName) ?? 0) + 1;
|
|
148
|
-
this.callCountByTool.set(toolName, count);
|
|
149
|
-
if (count % this.driftCheckIntervalCalls !== 0)
|
|
150
|
-
return;
|
|
151
|
-
const windowSize = this.driftWindowSize;
|
|
152
|
-
// Most-recent-first (see SqliteStore#listExecutions): the first half is
|
|
153
|
-
// the "recent" window, the second half the "baseline" it's compared
|
|
154
|
-
// against.
|
|
155
|
-
const executions = this.options.store.listExecutions({
|
|
156
|
-
serverId: this.options.serverId,
|
|
157
|
-
toolName,
|
|
158
|
-
limit: windowSize * 2
|
|
159
|
-
});
|
|
160
|
-
if (executions.length < windowSize * 2)
|
|
161
|
-
return;
|
|
162
|
-
const recent = executions.slice(0, windowSize);
|
|
163
|
-
const baseline = executions.slice(windowSize, windowSize * 2);
|
|
164
|
-
const shapeDrift = detectOutputShapeDrift({
|
|
165
|
-
serverId: this.options.serverId,
|
|
166
|
-
toolName,
|
|
167
|
-
baseline,
|
|
168
|
-
recent,
|
|
169
|
-
now
|
|
170
|
-
});
|
|
171
|
-
if (shapeDrift)
|
|
172
|
-
this.recordDrift(shapeDrift);
|
|
173
|
-
const latencyDrift = detectLatencyDrift({
|
|
174
|
-
serverId: this.options.serverId,
|
|
175
|
-
toolName,
|
|
176
|
-
baseline,
|
|
177
|
-
recent,
|
|
178
|
-
now
|
|
179
|
-
});
|
|
180
|
-
if (latencyDrift)
|
|
181
|
-
this.recordDrift(latencyDrift);
|
|
182
|
-
}
|
|
183
|
-
recordToolsListResult(result) {
|
|
184
|
-
const now = Date.now();
|
|
185
|
-
for (const tool of result.tools) {
|
|
186
|
-
if (typeof tool.name !== "string")
|
|
187
|
-
continue;
|
|
188
|
-
const fingerprint = computeSchemaFingerprint(tool.inputSchema);
|
|
189
|
-
const existing = this.options.store.getTool(this.options.serverId, tool.name);
|
|
190
|
-
const drift = detectSchemaDrift({
|
|
191
|
-
serverId: this.options.serverId,
|
|
192
|
-
toolName: tool.name,
|
|
193
|
-
previousFingerprint: existing?.schemaFingerprint,
|
|
194
|
-
currentFingerprint: fingerprint,
|
|
195
|
-
now
|
|
196
|
-
});
|
|
197
|
-
if (drift) {
|
|
198
|
-
this.recordDrift(drift);
|
|
199
|
-
}
|
|
200
|
-
const toolRecord = {
|
|
201
|
-
id: existing?.id ?? randomUUID(),
|
|
202
|
-
serverId: this.options.serverId,
|
|
203
|
-
name: tool.name,
|
|
204
|
-
description: tool.description,
|
|
205
|
-
schemaFingerprint: fingerprint,
|
|
206
|
-
inputSchema: tool.inputSchema,
|
|
207
|
-
firstSeenAt: existing?.firstSeenAt ?? now,
|
|
208
|
-
lastSeenAt: now
|
|
209
|
-
};
|
|
210
|
-
this.options.store.upsertTool(toolRecord);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
//# sourceMappingURL=proxy-collector.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-collector.js","sourceRoot":"","sources":["../src/proxy-collector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
2
|
-
import type { TransportConfig } from "@mattral/core";
|
|
3
|
-
/**
|
|
4
|
-
* Builds the appropriate official SDK client transport for a server's
|
|
5
|
-
* configured transport, used by {@link HealthChecker}. The stdio proxy
|
|
6
|
-
* collector does *not* use this — it talks to the child process directly
|
|
7
|
-
* (see docs/architecture.md#transport-adapters for why the two collection
|
|
8
|
-
* modes have different transport-handling needs).
|
|
9
|
-
*/
|
|
10
|
-
export declare function createClientTransport(config: TransportConfig): Transport;
|
|
11
|
-
//# sourceMappingURL=transport-factory.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport-factory.d.ts","sourceRoot":"","sources":["../src/transport-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAI/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAsBxE"}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
2
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
3
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
|
-
/**
|
|
5
|
-
* Builds the appropriate official SDK client transport for a server's
|
|
6
|
-
* configured transport, used by {@link HealthChecker}. The stdio proxy
|
|
7
|
-
* collector does *not* use this — it talks to the child process directly
|
|
8
|
-
* (see docs/architecture.md#transport-adapters for why the two collection
|
|
9
|
-
* modes have different transport-handling needs).
|
|
10
|
-
*/
|
|
11
|
-
export function createClientTransport(config) {
|
|
12
|
-
switch (config.type) {
|
|
13
|
-
case "stdio":
|
|
14
|
-
return new StdioClientTransport({
|
|
15
|
-
command: config.command,
|
|
16
|
-
args: config.args,
|
|
17
|
-
env: config.env,
|
|
18
|
-
cwd: config.cwd
|
|
19
|
-
});
|
|
20
|
-
case "sse":
|
|
21
|
-
return new SSEClientTransport(new URL(config.url), {
|
|
22
|
-
requestInit: config.headers ? { headers: config.headers } : undefined
|
|
23
|
-
});
|
|
24
|
-
case "http":
|
|
25
|
-
return new StreamableHTTPClientTransport(new URL(config.url), {
|
|
26
|
-
requestInit: config.headers ? { headers: config.headers } : undefined
|
|
27
|
-
});
|
|
28
|
-
default: {
|
|
29
|
-
const exhaustiveCheck = config;
|
|
30
|
-
throw new Error(`Unsupported transport config: ${JSON.stringify(exhaustiveCheck)}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
//# sourceMappingURL=transport-factory.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport-factory.js","sourceRoot":"","sources":["../src/transport-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAGnG;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAuB;IAC3D,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,OAAO;YACV,OAAO,IAAI,oBAAoB,CAAC;gBAC9B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,GAAG,EAAE,MAAM,CAAC,GAAG;aAChB,CAAC,CAAC;QACL,KAAK,KAAK;YACR,OAAO,IAAI,kBAAkB,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;gBACjD,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS;aACtE,CAAC,CAAC;QACL,KAAK,MAAM;YACT,OAAO,IAAI,6BAA6B,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;gBAC5D,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS;aACtE,CAAC,CAAC;QACL,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,eAAe,GAAU,MAAM,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;AACH,CAAC"}
|