@mcpfold/proxy 1.0.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 +28 -0
- package/dist/filter.d.ts +14 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +9 -0
- package/dist/filter.js.map +1 -0
- package/dist/handshake.d.ts +20 -0
- package/dist/handshake.d.ts.map +1 -0
- package/dist/handshake.js +67 -0
- package/dist/handshake.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonrpc.d.ts +30 -0
- package/dist/jsonrpc.d.ts.map +1 -0
- package/dist/jsonrpc.js +37 -0
- package/dist/jsonrpc.js.map +1 -0
- package/dist/proxy.d.ts +13 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +71 -0
- package/dist/proxy.js.map +1 -0
- package/dist/transport/memory.d.ts +17 -0
- package/dist/transport/memory.d.ts.map +1 -0
- package/dist/transport/memory.js +23 -0
- package/dist/transport/memory.js.map +1 -0
- package/dist/transport/stdio.d.ts +9 -0
- package/dist/transport/stdio.d.ts.map +1 -0
- package/dist/transport/stdio.js +36 -0
- package/dist/transport/stdio.js.map +1 -0
- package/dist/transport/types.d.ts +14 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +2 -0
- package/dist/transport/types.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mcpfold contributors
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
The MIT license above covers the open-source packages: packages/* (@mcpfold/core,
|
|
26
|
+
@mcpfold/adapters, @mcpfold/secrets, @mcpfold/proxy, @mcpfold/schema) and the `mcpfold`
|
|
27
|
+
CLI. The cloud layer (apps/web, services/edge) is commercial/closed and NOT covered by
|
|
28
|
+
this license.
|
package/dist/filter.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ToolsDirective } from '@mcpfold/core';
|
|
2
|
+
/**
|
|
3
|
+
* Tool allow/deny filtering (S5.2). `mode: "allow"` keeps only listed tools; `mode: "deny"`
|
|
4
|
+
* removes listed tools. Order is preserved and surviving tool objects (schemas) are passed
|
|
5
|
+
* through untouched.
|
|
6
|
+
*/
|
|
7
|
+
export interface McpTool {
|
|
8
|
+
name: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
export declare function isToolAllowed(name: string, directive: ToolsDirective): boolean;
|
|
12
|
+
/** Filter a tools array by the directive, preserving order and schemas. */
|
|
13
|
+
export declare function filterTools<T extends McpTool>(tools: T[], directive: ToolsDirective): T[];
|
|
14
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;;;GAIG;AAEH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,GAAG,OAAO,CAG9E;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,cAAc,GAAG,CAAC,EAAE,CAEzF"}
|
package/dist/filter.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function isToolAllowed(name, directive) {
|
|
2
|
+
const listed = directive.list.includes(name);
|
|
3
|
+
return directive.mode === 'allow' ? listed : !listed;
|
|
4
|
+
}
|
|
5
|
+
/** Filter a tools array by the directive, preserving order and schemas. */
|
|
6
|
+
export function filterTools(tools, directive) {
|
|
7
|
+
return tools.filter((tool) => isToolAllowed(tool.name, directive));
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,SAAyB;IACnE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AACvD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAoB,KAAU,EAAE,SAAyB;IAClF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MessageTransport } from './transport/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* MCP initialize + tools/list handshake (S10.4) over any {@link MessageTransport}. Transport-
|
|
4
|
+
* agnostic — the CLI `test` command drives it over a spawned stdio process or an HTTP transport,
|
|
5
|
+
* and tests drive it over an in-memory mock. Confirms a server actually initializes and can list
|
|
6
|
+
* its tools, with a bounded per-request timeout so a hung server aborts cleanly (S0.9).
|
|
7
|
+
*/
|
|
8
|
+
export declare const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
9
|
+
export interface HandshakeResult {
|
|
10
|
+
reachable: boolean;
|
|
11
|
+
protocolVersion?: string;
|
|
12
|
+
toolCount?: number;
|
|
13
|
+
/** Present when unreachable — a message safe to print (never the resolved token). */
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function handshake(transport: MessageTransport, opts?: {
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
clientName?: string;
|
|
19
|
+
}): Promise<HandshakeResult>;
|
|
20
|
+
//# sourceMappingURL=handshake.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../src/handshake.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;GAKG;AAEH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD,wBAAsB,SAAS,CAC7B,SAAS,EAAE,gBAAgB,EAC3B,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACrD,OAAO,CAAC,eAAe,CAAC,CAwD1B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { isResponse } from './jsonrpc.js';
|
|
2
|
+
/**
|
|
3
|
+
* MCP initialize + tools/list handshake (S10.4) over any {@link MessageTransport}. Transport-
|
|
4
|
+
* agnostic — the CLI `test` command drives it over a spawned stdio process or an HTTP transport,
|
|
5
|
+
* and tests drive it over an in-memory mock. Confirms a server actually initializes and can list
|
|
6
|
+
* its tools, with a bounded per-request timeout so a hung server aborts cleanly (S0.9).
|
|
7
|
+
*/
|
|
8
|
+
export const MCP_PROTOCOL_VERSION = '2024-11-05';
|
|
9
|
+
export async function handshake(transport, opts = {}) {
|
|
10
|
+
const timeoutMs = opts.timeoutMs ?? 10_000;
|
|
11
|
+
const pending = new Map();
|
|
12
|
+
transport.onMessage((msg) => {
|
|
13
|
+
if (!isResponse(msg) || msg.id == null)
|
|
14
|
+
return;
|
|
15
|
+
const p = pending.get(msg.id);
|
|
16
|
+
if (!p)
|
|
17
|
+
return;
|
|
18
|
+
pending.delete(msg.id);
|
|
19
|
+
if (msg.error)
|
|
20
|
+
p.reject(new Error(msg.error.message));
|
|
21
|
+
else
|
|
22
|
+
p.resolve(msg.result);
|
|
23
|
+
});
|
|
24
|
+
let nextId = 0;
|
|
25
|
+
const request = (method, params) => {
|
|
26
|
+
const id = ++nextId;
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const timer = setTimeout(() => {
|
|
29
|
+
pending.delete(id);
|
|
30
|
+
reject(new Error(`no response within ${timeoutMs}ms`));
|
|
31
|
+
}, timeoutMs);
|
|
32
|
+
pending.set(id, {
|
|
33
|
+
resolve: (v) => {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
resolve(v);
|
|
36
|
+
},
|
|
37
|
+
reject: (e) => {
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
reject(e);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
transport.send({ jsonrpc: '2.0', id, method, params });
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
try {
|
|
46
|
+
const init = (await request('initialize', {
|
|
47
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
48
|
+
capabilities: {},
|
|
49
|
+
clientInfo: { name: opts.clientName ?? 'mcpfold', version: '1' },
|
|
50
|
+
}));
|
|
51
|
+
// MCP requires the client to confirm initialization before other requests.
|
|
52
|
+
transport.send({ jsonrpc: '2.0', method: 'notifications/initialized' });
|
|
53
|
+
const tools = (await request('tools/list', {}));
|
|
54
|
+
return {
|
|
55
|
+
reachable: true,
|
|
56
|
+
protocolVersion: init?.protocolVersion,
|
|
57
|
+
toolCount: Array.isArray(tools?.tools) ? tools.tools.length : 0,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
return { reachable: false, error: err instanceof Error ? err.message : String(err) };
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
transport.close();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=handshake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handshake.js","sourceRoot":"","sources":["../src/handshake.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mcpfold/proxy — the local MCP proxy for tool-level curation. Transparent passthrough
|
|
3
|
+
* (S5.1) plus tools/list allow/deny filtering (S5.2).
|
|
4
|
+
*/
|
|
5
|
+
export { parseMessage, serializeMessage, errorResponse, isRequest, isResponse, type JsonRpcError, type JsonRpcId, type JsonRpcMessage, } from './jsonrpc.js';
|
|
6
|
+
export { connectProxy, type ProxyOptions } from './proxy.js';
|
|
7
|
+
export { filterTools, isToolAllowed, type McpTool } from './filter.js';
|
|
8
|
+
export { streamTransport } from './transport/stdio.js';
|
|
9
|
+
export { MemoryTransport } from './transport/memory.js';
|
|
10
|
+
export type { MessageTransport } from './transport/types.js';
|
|
11
|
+
export { handshake, MCP_PROTOCOL_VERSION, type HandshakeResult } from './handshake.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mcpfold/proxy — the local MCP proxy for tool-level curation. Transparent passthrough
|
|
3
|
+
* (S5.1) plus tools/list allow/deny filtering (S5.2).
|
|
4
|
+
*/
|
|
5
|
+
export { parseMessage, serializeMessage, errorResponse, isRequest, isResponse, } from './jsonrpc.js';
|
|
6
|
+
export { connectProxy } from './proxy.js';
|
|
7
|
+
export { filterTools, isToolAllowed } from './filter.js';
|
|
8
|
+
export { streamTransport } from './transport/stdio.js';
|
|
9
|
+
export { MemoryTransport } from './transport/memory.js';
|
|
10
|
+
export { handshake, MCP_PROTOCOL_VERSION } from './handshake.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,UAAU,GAIX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAqB,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAwB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal JSON-RPC 2.0 message model + newline-delimited framing (MCP stdio transport).
|
|
3
|
+
* The proxy is transport-agnostic: it operates on {@link JsonRpcMessage} objects and lets
|
|
4
|
+
* transports handle framing.
|
|
5
|
+
*/
|
|
6
|
+
export type JsonRpcId = string | number | null;
|
|
7
|
+
export interface JsonRpcError {
|
|
8
|
+
code: number;
|
|
9
|
+
message: string;
|
|
10
|
+
data?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface JsonRpcMessage {
|
|
13
|
+
jsonrpc: '2.0';
|
|
14
|
+
id?: JsonRpcId;
|
|
15
|
+
method?: string;
|
|
16
|
+
params?: unknown;
|
|
17
|
+
result?: unknown;
|
|
18
|
+
error?: JsonRpcError;
|
|
19
|
+
}
|
|
20
|
+
/** Parse one newline-delimited JSON message; returns null on malformed input. */
|
|
21
|
+
export declare function parseMessage(line: string): JsonRpcMessage | null;
|
|
22
|
+
/** Serialize a message with a trailing newline (MCP stdio framing). */
|
|
23
|
+
export declare function serializeMessage(message: JsonRpcMessage): string;
|
|
24
|
+
/** True if the message is a request carrying the given method. */
|
|
25
|
+
export declare function isRequest(message: JsonRpcMessage, method?: string): boolean;
|
|
26
|
+
/** True if the message is a response (has result or error) to a request id. */
|
|
27
|
+
export declare function isResponse(message: JsonRpcMessage): boolean;
|
|
28
|
+
/** Build a JSON-RPC error response for a request id. */
|
|
29
|
+
export declare function errorResponse(id: JsonRpcId, error: JsonRpcError): JsonRpcMessage;
|
|
30
|
+
//# sourceMappingURL=jsonrpc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.d.ts","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAShE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAEhE;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAG3E;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAE3D;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,cAAc,CAEhF"}
|
package/dist/jsonrpc.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal JSON-RPC 2.0 message model + newline-delimited framing (MCP stdio transport).
|
|
3
|
+
* The proxy is transport-agnostic: it operates on {@link JsonRpcMessage} objects and lets
|
|
4
|
+
* transports handle framing.
|
|
5
|
+
*/
|
|
6
|
+
/** Parse one newline-delimited JSON message; returns null on malformed input. */
|
|
7
|
+
export function parseMessage(line) {
|
|
8
|
+
const trimmed = line.trim();
|
|
9
|
+
if (!trimmed)
|
|
10
|
+
return null;
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(trimmed);
|
|
13
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Serialize a message with a trailing newline (MCP stdio framing). */
|
|
20
|
+
export function serializeMessage(message) {
|
|
21
|
+
return `${JSON.stringify(message)}\n`;
|
|
22
|
+
}
|
|
23
|
+
/** True if the message is a request carrying the given method. */
|
|
24
|
+
export function isRequest(message, method) {
|
|
25
|
+
if (typeof message.method !== 'string')
|
|
26
|
+
return false;
|
|
27
|
+
return method === undefined ? true : message.method === method;
|
|
28
|
+
}
|
|
29
|
+
/** True if the message is a response (has result or error) to a request id. */
|
|
30
|
+
export function isResponse(message) {
|
|
31
|
+
return message.id !== undefined && (message.result !== undefined || message.error !== undefined);
|
|
32
|
+
}
|
|
33
|
+
/** Build a JSON-RPC error response for a request id. */
|
|
34
|
+
export function errorResponse(id, error) {
|
|
35
|
+
return { jsonrpc: '2.0', id, error };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=jsonrpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.js","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmBH,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAmB,CAAC;QACrD,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,OAAuB;IACtD,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;AACxC,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,SAAS,CAAC,OAAuB,EAAE,MAAe;IAChE,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACrD,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC;AACjE,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,OAAuB;IAChD,OAAO,OAAO,CAAC,EAAE,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;AACnG,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,EAAa,EAAE,KAAmB;IAC9D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;AACvC,CAAC"}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ToolsDirective } from '@mcpfold/core';
|
|
2
|
+
import type { MessageTransport } from './transport/types.js';
|
|
3
|
+
export interface ProxyOptions {
|
|
4
|
+
tools?: ToolsDirective;
|
|
5
|
+
/** Reject `tools/call` to a filtered-out tool instead of forwarding it. Default true. */
|
|
6
|
+
rejectFilteredCalls?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Connect a client transport to a server transport, forwarding messages and applying the
|
|
10
|
+
* optional tools directive. Returns a disposer that closes both transports.
|
|
11
|
+
*/
|
|
12
|
+
export declare function connectProxy(client: MessageTransport, server: MessageTransport, options?: ProxyOptions): () => void;
|
|
13
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAiB7D,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAoBD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE,YAAiB,GACzB,MAAM,IAAI,CAkDZ"}
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { errorResponse, isRequest } from './jsonrpc.js';
|
|
2
|
+
import { filterTools, isToolAllowed } from './filter.js';
|
|
3
|
+
/**
|
|
4
|
+
* The mcpfold MCP proxy (S5.1 core + S5.2 filtering).
|
|
5
|
+
*
|
|
6
|
+
* Wires a client-side transport to a server-side transport and forwards every message
|
|
7
|
+
* faithfully in both directions (request ids, capabilities, notifications, and errors are
|
|
8
|
+
* preserved). When a `tools` directive is supplied it additionally:
|
|
9
|
+
* - filters `tools/list` RESPONSES to the allow/deny set, and
|
|
10
|
+
* - (optionally) rejects a `tools/call` to a filtered-out tool with a clear MCP error,
|
|
11
|
+
* without forwarding it to the real server.
|
|
12
|
+
* With no directive it is a pure passthrough (S5.1). Opt-in; off by default.
|
|
13
|
+
*/
|
|
14
|
+
// MCP error code for "method/tool not found".
|
|
15
|
+
const METHOD_NOT_FOUND = -32601;
|
|
16
|
+
function isToolsResult(result) {
|
|
17
|
+
return (typeof result === 'object' &&
|
|
18
|
+
result !== null &&
|
|
19
|
+
Array.isArray(result.tools));
|
|
20
|
+
}
|
|
21
|
+
function callToolName(message) {
|
|
22
|
+
const params = message.params;
|
|
23
|
+
return typeof params?.name === 'string' ? params.name : undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Connect a client transport to a server transport, forwarding messages and applying the
|
|
27
|
+
* optional tools directive. Returns a disposer that closes both transports.
|
|
28
|
+
*/
|
|
29
|
+
export function connectProxy(client, server, options = {}) {
|
|
30
|
+
const directive = options.tools;
|
|
31
|
+
const rejectFiltered = options.rejectFilteredCalls ?? true;
|
|
32
|
+
// Track which in-flight request ids were `tools/list` so we filter only their responses.
|
|
33
|
+
const pendingToolsList = new Set();
|
|
34
|
+
client.onMessage((message) => {
|
|
35
|
+
// Reject calls to filtered-out tools before they ever reach the real server.
|
|
36
|
+
if (directive &&
|
|
37
|
+
rejectFiltered &&
|
|
38
|
+
isRequest(message, 'tools/call') &&
|
|
39
|
+
message.id !== undefined) {
|
|
40
|
+
const name = callToolName(message);
|
|
41
|
+
if (name && !isToolAllowed(name, directive)) {
|
|
42
|
+
client.send(errorResponse(message.id, {
|
|
43
|
+
code: METHOD_NOT_FOUND,
|
|
44
|
+
message: `Tool "${name}" is not available (filtered by mcpfold).`,
|
|
45
|
+
}));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (directive && isRequest(message, 'tools/list') && message.id !== undefined) {
|
|
50
|
+
pendingToolsList.add(message.id);
|
|
51
|
+
}
|
|
52
|
+
server.send(message);
|
|
53
|
+
});
|
|
54
|
+
server.onMessage((message) => {
|
|
55
|
+
if (directive &&
|
|
56
|
+
message.id !== undefined &&
|
|
57
|
+
pendingToolsList.has(message.id) &&
|
|
58
|
+
isToolsResult(message.result)) {
|
|
59
|
+
pendingToolsList.delete(message.id);
|
|
60
|
+
const filtered = filterTools(message.result.tools, directive);
|
|
61
|
+
client.send({ ...message, result: { ...message.result, tools: filtered } });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
client.send(message);
|
|
65
|
+
});
|
|
66
|
+
return () => {
|
|
67
|
+
client.close();
|
|
68
|
+
server.close();
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAuC,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,WAAW,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AAGvE;;;;;;;;;;GAUG;AAEH,8CAA8C;AAC9C,MAAM,gBAAgB,GAAG,CAAC,KAAK,CAAC;AAahC,SAAS,aAAa,CAAC,MAAe;IACpC,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,KAAK,CAAC,OAAO,CAAE,MAA8B,CAAC,KAAK,CAAC,CACrD,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,OAAuB;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAwC,CAAC;IAChE,OAAO,OAAO,MAAM,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAwB,EACxB,MAAwB,EACxB,UAAwB,EAAE;IAE1B,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC;IAChC,MAAM,cAAc,GAAG,OAAO,CAAC,mBAAmB,IAAI,IAAI,CAAC;IAC3D,yFAAyF;IACzF,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAa,CAAC;IAE9C,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,6EAA6E;QAC7E,IACE,SAAS;YACT,cAAc;YACd,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC;YAChC,OAAO,CAAC,EAAE,KAAK,SAAS,EACxB,CAAC;YACD,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;YACnC,IAAI,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;gBAC5C,MAAM,CAAC,IAAI,CACT,aAAa,CAAC,OAAO,CAAC,EAAE,EAAE;oBACxB,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,SAAS,IAAI,2CAA2C;iBAClE,CAAC,CACH,CAAC;gBACF,OAAO;YACT,CAAC;QACH,CAAC;QACD,IAAI,SAAS,IAAI,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC9E,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,IACE,SAAS;YACT,OAAO,CAAC,EAAE,KAAK,SAAS;YACxB,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,EAC7B,CAAC;YACD,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACpC,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YAC9D,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { JsonRpcMessage } from '../jsonrpc.js';
|
|
2
|
+
import type { MessageTransport } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* In-memory transport for tests: `receive()` simulates a message arriving from the peer, and
|
|
5
|
+
* `sent` records everything the proxy wrote toward the peer.
|
|
6
|
+
*/
|
|
7
|
+
export declare class MemoryTransport implements MessageTransport {
|
|
8
|
+
readonly sent: JsonRpcMessage[];
|
|
9
|
+
private handler?;
|
|
10
|
+
closed: boolean;
|
|
11
|
+
onMessage(handler: (message: JsonRpcMessage) => void): void;
|
|
12
|
+
send(message: JsonRpcMessage): void;
|
|
13
|
+
/** Simulate a message arriving from the peer (drives the proxy). */
|
|
14
|
+
receive(message: JsonRpcMessage): void;
|
|
15
|
+
close(): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/transport/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,CAAM;IACrC,OAAO,CAAC,OAAO,CAAC,CAAoC;IACpD,MAAM,UAAS;IAEf,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI;IAI3D,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAInC,oEAAoE;IACpE,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAItC,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory transport for tests: `receive()` simulates a message arriving from the peer, and
|
|
3
|
+
* `sent` records everything the proxy wrote toward the peer.
|
|
4
|
+
*/
|
|
5
|
+
export class MemoryTransport {
|
|
6
|
+
sent = [];
|
|
7
|
+
handler;
|
|
8
|
+
closed = false;
|
|
9
|
+
onMessage(handler) {
|
|
10
|
+
this.handler = handler;
|
|
11
|
+
}
|
|
12
|
+
send(message) {
|
|
13
|
+
this.sent.push(message);
|
|
14
|
+
}
|
|
15
|
+
/** Simulate a message arriving from the peer (drives the proxy). */
|
|
16
|
+
receive(message) {
|
|
17
|
+
this.handler?.(message);
|
|
18
|
+
}
|
|
19
|
+
close() {
|
|
20
|
+
this.closed = true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/transport/memory.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,OAAO,eAAe;IACjB,IAAI,GAAqB,EAAE,CAAC;IAC7B,OAAO,CAAqC;IACpD,MAAM,GAAG,KAAK,CAAC;IAEf,SAAS,CAAC,OAA0C;QAClD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,IAAI,CAAC,OAAuB;QAC1B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED,oEAAoE;IACpE,OAAO,CAAC,OAAuB;QAC7B,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Readable, Writable } from 'node:stream';
|
|
2
|
+
import type { MessageTransport } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Stdio transport: reads newline-delimited JSON-RPC messages from a Readable and writes them
|
|
5
|
+
* to a Writable. Used to bridge the parent process stdio (the MCP client) and a spawned
|
|
6
|
+
* server child's stdio.
|
|
7
|
+
*/
|
|
8
|
+
export declare function streamTransport(input: Readable, output: Writable): MessageTransport;
|
|
9
|
+
//# sourceMappingURL=stdio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/transport/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,gBAAgB,CA8BnF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { parseMessage, serializeMessage } from '../jsonrpc.js';
|
|
2
|
+
/**
|
|
3
|
+
* Stdio transport: reads newline-delimited JSON-RPC messages from a Readable and writes them
|
|
4
|
+
* to a Writable. Used to bridge the parent process stdio (the MCP client) and a spawned
|
|
5
|
+
* server child's stdio.
|
|
6
|
+
*/
|
|
7
|
+
export function streamTransport(input, output) {
|
|
8
|
+
let handler;
|
|
9
|
+
let buffer = '';
|
|
10
|
+
const onData = (chunk) => {
|
|
11
|
+
buffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
12
|
+
let newlineIndex = buffer.indexOf('\n');
|
|
13
|
+
while (newlineIndex !== -1) {
|
|
14
|
+
const line = buffer.slice(0, newlineIndex);
|
|
15
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
16
|
+
const message = parseMessage(line);
|
|
17
|
+
if (message && handler)
|
|
18
|
+
handler(message);
|
|
19
|
+
newlineIndex = buffer.indexOf('\n');
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
input.setEncoding('utf8');
|
|
23
|
+
input.on('data', onData);
|
|
24
|
+
return {
|
|
25
|
+
onMessage(h) {
|
|
26
|
+
handler = h;
|
|
27
|
+
},
|
|
28
|
+
send(message) {
|
|
29
|
+
output.write(serializeMessage(message));
|
|
30
|
+
},
|
|
31
|
+
close() {
|
|
32
|
+
input.off('data', onData);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transport/stdio.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAuB,MAAM,eAAe,CAAC;AAGpF;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAe,EAAE,MAAgB;IAC/D,IAAI,OAAwD,CAAC;IAC7D,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,MAAM,MAAM,GAAG,CAAC,KAAsB,EAAQ,EAAE;QAC9C,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrE,IAAI,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,OAAO,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;YAC3C,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,OAAO,IAAI,OAAO;gBAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YACzC,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC;IAEF,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEzB,OAAO;QACL,SAAS,CAAC,CAAC;YACT,OAAO,GAAG,CAAC,CAAC;QACd,CAAC;QACD,IAAI,CAAC,OAAO;YACV,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK;YACH,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { JsonRpcMessage } from '../jsonrpc.js';
|
|
2
|
+
/**
|
|
3
|
+
* A bidirectional MCP message transport. The proxy wires two of these together (client-side
|
|
4
|
+
* and server-side). Concrete transports (stdio streams, in-memory test) implement it.
|
|
5
|
+
*/
|
|
6
|
+
export interface MessageTransport {
|
|
7
|
+
/** Register the handler for messages arriving from the peer. Called once. */
|
|
8
|
+
onMessage(handler: (message: JsonRpcMessage) => void): void;
|
|
9
|
+
/** Send a message to the peer. */
|
|
10
|
+
send(message: JsonRpcMessage): void;
|
|
11
|
+
/** Close the transport and release resources. */
|
|
12
|
+
close(): void;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/transport/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5D,kCAAkC;IAClC,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACpC,iDAAiD;IACjD,KAAK,IAAI,IAAI,CAAC;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/transport/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mcpfold/proxy",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Thin local MCP proxy for tool-level curation: transparent passthrough + tools/list filtering.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@mcpfold/core": "1.0.0"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/dj-pearson/MCPFold.git",
|
|
27
|
+
"directory": "packages/proxy"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://mcpfold.com",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/dj-pearson/MCPFold/issues"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc -p tsconfig.build.json",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"lint": "eslint . --max-warnings=0",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"bench": "WRITE_BENCH=1 vitest run test/benchmark.test.ts"
|
|
39
|
+
}
|
|
40
|
+
}
|