mcpose 1.0.0 → 1.1.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 +34 -1
- package/dist/backendClient.d.ts +8 -21
- package/dist/backendClient.d.ts.map +1 -1
- package/dist/backendClient.js +4 -17
- package/dist/backendClient.js.map +1 -1
- package/dist/core.d.ts +49 -53
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +91 -38
- package/dist/core.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +10 -42
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +9 -34
- package/dist/middleware.js.map +1 -1
- package/dist/testing.d.ts +11 -43
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +3 -22
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -190,6 +190,39 @@ function createProxyServer(backend: BackendClient, options?: ProxyOptions): Serv
|
|
|
190
190
|
|
|
191
191
|
---
|
|
192
192
|
|
|
193
|
+
### `HttpProxyOptions` · `startHttpProxy()`
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
interface HttpProxyOptions {
|
|
197
|
+
port?: number; // Default: 3000
|
|
198
|
+
host?: string; // Default: all interfaces
|
|
199
|
+
path?: string; // Default: '/mcp'
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function startHttpProxy(
|
|
203
|
+
backend: BackendClient,
|
|
204
|
+
options?: ProxyOptions,
|
|
205
|
+
httpOptions?: HttpProxyOptions,
|
|
206
|
+
): Promise<http.Server>;
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Starts the proxy over Streamable HTTP with stateful sessions. Each client connection is assigned an `mcp-session-id`; upstream list-change notifications (`tools/list_changed`, `resources/list_changed`, `prompts/list_changed`) are fanned out to all active sessions.
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
import { createBackendClient, startHttpProxy } from 'mcpose';
|
|
213
|
+
|
|
214
|
+
const backend = await createBackendClient({ url: 'http://upstream-mcp-server/mcp' });
|
|
215
|
+
const server = await startHttpProxy(backend, { toolMiddleware: [loggingMW] }, { port: 8080 });
|
|
216
|
+
// HTTP server is now listening on port 8080 at /mcp
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Limitations:**
|
|
220
|
+
- Two `startHttpProxy` calls sharing the same `backend` will overwrite each other's notification handlers (last call wins).
|
|
221
|
+
- Sessions are only cleaned up on `DELETE` or server close — there is no idle timeout.
|
|
222
|
+
- SSE reconnect replay is not supported (no `EventStore`).
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
193
226
|
## Recipe: PII redaction
|
|
194
227
|
|
|
195
228
|
The origin use case for mcpose: a financial-grade MCP server where every Elasticsearch tool response must be scrubbed of PII before it reaches the LLM or the audit log.
|
|
@@ -239,7 +272,7 @@ The array order guarantees: PII is redacted *before* the audit layer ever sees t
|
|
|
239
272
|
|
|
240
273
|
## Roadmap
|
|
241
274
|
|
|
242
|
-
- [
|
|
275
|
+
- [x] **HTTP/SSE server transport** — `startHttpProxy()` adds a Streamable HTTP server-side transport with stateful sessions
|
|
243
276
|
- [ ] **ATXP protocol support** — enable MCP monetization by implementing the ATXP (Agent Transaction Protocol) standard, letting tool providers attach pricing and billing metadata to responses
|
|
244
277
|
|
|
245
278
|
---
|
package/dist/backendClient.d.ts
CHANGED
|
@@ -1,34 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Supports two transport modes:
|
|
5
|
-
* - **stdio** (default): spawns the backend server as a child process.
|
|
6
|
-
* - **HTTP/SSE**: connects to an already-running HTTP MCP server.
|
|
7
|
-
*
|
|
8
|
-
* Returns a connected `@modelcontextprotocol/sdk` Client, which exposes the
|
|
9
|
-
* full MCP protocol surface (listTools, callTool, listResources, readResource,
|
|
10
|
-
* listPrompts, getPrompt) with exact MCP type fidelity — important for a
|
|
11
|
-
* transparent proxy that must not transform protocol shapes.
|
|
12
|
-
*
|
|
13
|
-
* @module
|
|
2
|
+
* Backend MCP client factory.
|
|
3
|
+
* Modes: stdio (spawns child process) or HTTP/SSE (connects to running server).
|
|
14
4
|
*/
|
|
15
5
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
16
|
-
/**
|
|
6
|
+
/** Backend connection config. */
|
|
17
7
|
export interface BackendConfig {
|
|
18
|
-
/** Shell command to spawn
|
|
8
|
+
/** Shell command to spawn backend (e.g. `"node"`). Required if no `url`. */
|
|
19
9
|
command?: string;
|
|
20
|
-
/**
|
|
10
|
+
/** Args passed to command (e.g. `["/path/to/server.mjs"]`). */
|
|
21
11
|
args?: string[];
|
|
22
|
-
/** HTTP/SSE URL of
|
|
12
|
+
/** HTTP/SSE URL of running backend. Takes precedence over stdio. */
|
|
23
13
|
url?: string;
|
|
24
14
|
}
|
|
25
15
|
export type BackendClient = Client;
|
|
26
16
|
/**
|
|
27
|
-
* Creates and connects an MCP client to the backend
|
|
28
|
-
*
|
|
29
|
-
* @param config - Backend connection details (stdio or HTTP).
|
|
30
|
-
* @returns A connected MCP SDK Client ready for tool/resource/prompt calls.
|
|
31
|
-
* @throws If neither `command` nor `url` is provided, or if the connection fails.
|
|
17
|
+
* Creates and connects an MCP client to the backend.
|
|
18
|
+
* @throws If neither `command` nor `url` provided, or connection fails.
|
|
32
19
|
*/
|
|
33
20
|
export declare function createBackendClient(config: BackendConfig): Promise<BackendClient>;
|
|
34
21
|
//# sourceMappingURL=backendClient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backendClient.d.ts","sourceRoot":"","sources":["../src/backendClient.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"backendClient.d.ts","sourceRoot":"","sources":["../src/backendClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAEnC;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,aAAa,CAAC,CAqBxB"}
|
package/dist/backendClient.js
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Supports two transport modes:
|
|
5
|
-
* - **stdio** (default): spawns the backend server as a child process.
|
|
6
|
-
* - **HTTP/SSE**: connects to an already-running HTTP MCP server.
|
|
7
|
-
*
|
|
8
|
-
* Returns a connected `@modelcontextprotocol/sdk` Client, which exposes the
|
|
9
|
-
* full MCP protocol surface (listTools, callTool, listResources, readResource,
|
|
10
|
-
* listPrompts, getPrompt) with exact MCP type fidelity — important for a
|
|
11
|
-
* transparent proxy that must not transform protocol shapes.
|
|
12
|
-
*
|
|
13
|
-
* @module
|
|
2
|
+
* Backend MCP client factory.
|
|
3
|
+
* Modes: stdio (spawns child process) or HTTP/SSE (connects to running server).
|
|
14
4
|
*/
|
|
15
5
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
16
6
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
17
7
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
18
8
|
/**
|
|
19
|
-
* Creates and connects an MCP client to the backend
|
|
20
|
-
*
|
|
21
|
-
* @param config - Backend connection details (stdio or HTTP).
|
|
22
|
-
* @returns A connected MCP SDK Client ready for tool/resource/prompt calls.
|
|
23
|
-
* @throws If neither `command` nor `url` is provided, or if the connection fails.
|
|
9
|
+
* Creates and connects an MCP client to the backend.
|
|
10
|
+
* @throws If neither `command` nor `url` provided, or connection fails.
|
|
24
11
|
*/
|
|
25
12
|
export async function createBackendClient(config) {
|
|
26
13
|
if (!config.command && !config.url) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backendClient.js","sourceRoot":"","sources":["../src/backendClient.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"backendClient.js","sourceRoot":"","sources":["../src/backendClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAcnG;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAqB;IAErB,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,iEAAiE,CAClE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAE,EAC5C,EAAE,YAAY,EAAE,EAAE,EAAE,CACrB,CAAC;IAEF,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG;QAC1B,CAAC,CAAC,IAAI,6BAA6B,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACxD,CAAC,CAAC,IAAI,oBAAoB,CAAC;YACvB,OAAO,EAAE,MAAM,CAAC,OAAQ;YACxB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;SACxB,CAAC,CAAC;IAEP,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/core.d.ts
CHANGED
|
@@ -1,85 +1,81 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Wires an MCP server (exposed to the LLM) to an upstream MCP client,
|
|
5
|
-
* applying composed middleware pipelines to tool calls and resource reads.
|
|
6
|
-
*
|
|
7
|
-
* @module
|
|
8
|
-
*/
|
|
1
|
+
/** MCP proxy core: wires server→upstream through middleware pipelines. */
|
|
2
|
+
import * as http from 'node:http';
|
|
9
3
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
4
|
import { type CallToolRequest, type CallToolResult, type CompatibilityCallToolResult, type ReadResourceRequest, type ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
|
|
11
5
|
import { type Middleware } from './middleware.js';
|
|
12
6
|
import type { BackendClient } from './backendClient.js';
|
|
13
7
|
/**
|
|
14
|
-
* Middleware for
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* union that includes the legacy `{ toolResult }` shape (protocol 2024-10-07).
|
|
18
|
-
* Middleware implementations should narrow with `hasToolContent(result)` before
|
|
19
|
-
* accessing `.content` or `.isError`.
|
|
8
|
+
* Middleware for tool calls.
|
|
9
|
+
* Uses `CompatibilityCallToolResult` to cover legacy `{ toolResult }` shape
|
|
10
|
+
* (protocol 2024-10-07). Narrow with `hasToolContent()` before accessing `.content`.
|
|
20
11
|
*/
|
|
21
12
|
export type ToolMiddleware = Middleware<CallToolRequest, CompatibilityCallToolResult>;
|
|
22
|
-
/** Middleware for
|
|
13
|
+
/** Middleware for resource reads. */
|
|
23
14
|
export type ResourceMiddleware = Middleware<ReadResourceRequest, ReadResourceResult>;
|
|
24
15
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* Use this in middleware implementations to safely access `.content` and
|
|
29
|
-
* `.isError` without casts, since both union members carry an index signature
|
|
30
|
-
* (`[x: string]: unknown`) that would otherwise make property access `unknown`.
|
|
16
|
+
* Narrows `CompatibilityCallToolResult` to `CallToolResult` (has `.content` array).
|
|
17
|
+
* Both union members carry `[x: string]: unknown`, so this avoids unsafe casts.
|
|
31
18
|
*/
|
|
32
19
|
export declare function hasToolContent(r: CompatibilityCallToolResult): r is CallToolResult;
|
|
33
|
-
/**
|
|
20
|
+
/** HTTP transport options for {@link startHttpProxy}. */
|
|
21
|
+
export interface HttpProxyOptions {
|
|
22
|
+
/** Default: 3000 */
|
|
23
|
+
port?: number;
|
|
24
|
+
/** Default: all interfaces */
|
|
25
|
+
host?: string;
|
|
26
|
+
/** Default: '/mcp' */
|
|
27
|
+
path?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Proxy server options. */
|
|
34
30
|
export interface ProxyOptions {
|
|
35
31
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* `pipe()` is called internally — no need to wrap manually.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* toolMiddleware: [piiMW, auditMW] // pii redacts first, audit logs clean data
|
|
32
|
+
* Tool middleware in response-processing order (first = innermost).
|
|
33
|
+
* @example [piiMW, auditMW] // pii redacts first, audit logs clean data
|
|
42
34
|
*/
|
|
43
35
|
toolMiddleware?: ReadonlyArray<ToolMiddleware>;
|
|
44
|
-
/**
|
|
45
|
-
* Ordered middleware stack for resource reads in response-processing order.
|
|
46
|
-
* The first element processes the response first (innermost layer).
|
|
47
|
-
*/
|
|
36
|
+
/** Resource middleware in response-processing order (first = innermost). */
|
|
48
37
|
resourceMiddleware?: ReadonlyArray<ResourceMiddleware>;
|
|
49
|
-
/**
|
|
38
|
+
/** Tools that skip middleware — upstream response forwarded as-is. */
|
|
50
39
|
passThroughTools?: ReadonlyArray<string>;
|
|
51
|
-
/**
|
|
40
|
+
/** Resources that skip middleware — upstream response forwarded as-is. */
|
|
52
41
|
passThroughResources?: ReadonlyArray<string>;
|
|
53
|
-
/**
|
|
42
|
+
/** Tools hidden from list_tools and rejected at runtime with MethodNotFound. */
|
|
54
43
|
hiddenTools?: ReadonlyArray<string>;
|
|
55
|
-
/**
|
|
44
|
+
/** Resources hidden from list_resources and rejected at runtime with InvalidRequest. */
|
|
56
45
|
hiddenResources?: ReadonlyArray<string>;
|
|
57
46
|
}
|
|
58
47
|
/**
|
|
59
|
-
* Creates
|
|
48
|
+
* Creates a proxy MCP server without connecting it to a transport.
|
|
60
49
|
*
|
|
61
|
-
* Mirrors
|
|
62
|
-
*
|
|
63
|
-
* forwarded as-is (no middleware applied).
|
|
50
|
+
* Mirrors upstream tool/resource/prompt lists and routes requests through
|
|
51
|
+
* middleware pipelines. Prompts are forwarded as-is.
|
|
64
52
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* the registered handlers without spawning a stdio transport.
|
|
53
|
+
* Uses low-level `Server` (not `McpServer`) — transparent proxying requires
|
|
54
|
+
* generic list interception; `McpServer.tool()` needs names upfront.
|
|
68
55
|
*
|
|
69
|
-
* @param
|
|
70
|
-
* @param options
|
|
71
|
-
* @returns
|
|
56
|
+
* @param backend - Connected (or mock) upstream MCP client.
|
|
57
|
+
* @param options - Middleware stacks, hidden/passthrough sets.
|
|
58
|
+
* @returns Configured {@link Server} ready to connect.
|
|
72
59
|
*/
|
|
73
60
|
export declare function createProxyServer(backend: BackendClient, options?: ProxyOptions): Server;
|
|
74
61
|
/**
|
|
75
|
-
* Starts the proxy
|
|
62
|
+
* Starts the proxy on stdio.
|
|
63
|
+
* Calls {@link createProxyServer} then connects to `StdioServerTransport`.
|
|
64
|
+
* Use `createProxyServer` directly for testable access to the server.
|
|
65
|
+
*/
|
|
66
|
+
export declare function startProxy(backend: BackendClient, options?: ProxyOptions): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Starts the proxy over Streamable HTTP with stateful sessions.
|
|
69
|
+
*
|
|
70
|
+
* Sessions keyed by `mcp-session-id`. Upstream notifications fanned out to
|
|
71
|
+
* all active sessions via their GET SSE stream.
|
|
76
72
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
73
|
+
* **Limitations:**
|
|
74
|
+
* - Two calls with same `backend` overwrite notification handlers (last wins).
|
|
75
|
+
* - Sessions never expire — only cleaned up on DELETE or server close.
|
|
76
|
+
* - No `EventStore` → SSE reconnect replay unsupported.
|
|
80
77
|
*
|
|
81
|
-
* @
|
|
82
|
-
* @param options - Optional middleware stacks for tools and resources.
|
|
78
|
+
* @returns Promise resolving to the listening `http.Server`.
|
|
83
79
|
*/
|
|
84
|
-
export declare function
|
|
80
|
+
export declare function startHttpProxy(backend: BackendClient, options?: ProxyOptions, httpOptions?: HttpProxyOptions): Promise<http.Server>;
|
|
85
81
|
//# sourceMappingURL=core.d.ts.map
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGnE,OAAO,EAYL,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,CACrC,eAAe,EACf,2BAA2B,CAC5B,CAAC;AAEF,qCAAqC;AACrC,MAAM,MAAM,kBAAkB,GAAG,UAAU,CACzC,mBAAmB,EACnB,kBAAkB,CACnB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,2BAA2B,GAC7B,CAAC,IAAI,cAAc,CAErB;AAED,yDAAyD;AACzD,MAAM,WAAW,gBAAgB;IAC/B,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,4BAA4B;AAC5B,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAE/C,4EAA4E;IAC5E,kBAAkB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAEvD,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEzC,0EAA0E;IAC1E,oBAAoB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAE7C,gFAAgF;IAChF,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEpC,wFAAwF;IACxF,eAAe,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,YAAiB,GACzB,MAAM,CA+DR;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,YAAiB,EAC1B,WAAW,GAAE,gBAAqB,GACjC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAiEtB"}
|
package/dist/core.js
CHANGED
|
@@ -1,40 +1,30 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
* Wires an MCP server (exposed to the LLM) to an upstream MCP client,
|
|
5
|
-
* applying composed middleware pipelines to tool calls and resource reads.
|
|
6
|
-
*
|
|
7
|
-
* @module
|
|
8
|
-
*/
|
|
1
|
+
/** MCP proxy core: wires server→upstream through middleware pipelines. */
|
|
2
|
+
import * as http from 'node:http';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
9
4
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
|
-
import {
|
|
6
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
7
|
+
import { CallToolRequestSchema, ErrorCode, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, PromptListChangedNotificationSchema, ReadResourceRequestSchema, ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
12
8
|
import { pipe } from './middleware.js';
|
|
13
9
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Use this in middleware implementations to safely access `.content` and
|
|
18
|
-
* `.isError` without casts, since both union members carry an index signature
|
|
19
|
-
* (`[x: string]: unknown`) that would otherwise make property access `unknown`.
|
|
10
|
+
* Narrows `CompatibilityCallToolResult` to `CallToolResult` (has `.content` array).
|
|
11
|
+
* Both union members carry `[x: string]: unknown`, so this avoids unsafe casts.
|
|
20
12
|
*/
|
|
21
13
|
export function hasToolContent(r) {
|
|
22
14
|
return Array.isArray(r.content);
|
|
23
15
|
}
|
|
24
16
|
/**
|
|
25
|
-
* Creates
|
|
17
|
+
* Creates a proxy MCP server without connecting it to a transport.
|
|
26
18
|
*
|
|
27
|
-
* Mirrors
|
|
28
|
-
*
|
|
29
|
-
* forwarded as-is (no middleware applied).
|
|
19
|
+
* Mirrors upstream tool/resource/prompt lists and routes requests through
|
|
20
|
+
* middleware pipelines. Prompts are forwarded as-is.
|
|
30
21
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* the registered handlers without spawning a stdio transport.
|
|
22
|
+
* Uses low-level `Server` (not `McpServer`) — transparent proxying requires
|
|
23
|
+
* generic list interception; `McpServer.tool()` needs names upfront.
|
|
34
24
|
*
|
|
35
|
-
* @param
|
|
36
|
-
* @param options
|
|
37
|
-
* @returns
|
|
25
|
+
* @param backend - Connected (or mock) upstream MCP client.
|
|
26
|
+
* @param options - Middleware stacks, hidden/passthrough sets.
|
|
27
|
+
* @returns Configured {@link Server} ready to connect.
|
|
38
28
|
*/
|
|
39
29
|
export function createProxyServer(backend, options = {}) {
|
|
40
30
|
const toolPipeline = pipe(options.toolMiddleware ?? []);
|
|
@@ -43,11 +33,6 @@ export function createProxyServer(backend, options = {}) {
|
|
|
43
33
|
const passThroughToolSet = new Set(options.passThroughTools ?? []);
|
|
44
34
|
const hiddenResourceSet = new Set(options.hiddenResources ?? []);
|
|
45
35
|
const passThroughResourceSet = new Set(options.passThroughResources ?? []);
|
|
46
|
-
// NOTE: Using the low-level Server intentionally — a transparent proxy must
|
|
47
|
-
// intercept list_tools / list_resources generically without knowing tool names
|
|
48
|
-
// upfront. McpServer.tool() requires pre-registering each tool by name, which
|
|
49
|
-
// breaks dynamic forwarding. The SDK explicitly carves out this pattern:
|
|
50
|
-
// "Only use Server for advanced use cases."
|
|
51
36
|
const server = new Server({ name: 'mcpose', version: '1.0.0' }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
52
37
|
// ── Tool handlers ──────────────────────────────────────────────────────────
|
|
53
38
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -89,17 +74,85 @@ export function createProxyServer(backend, options = {}) {
|
|
|
89
74
|
return server;
|
|
90
75
|
}
|
|
91
76
|
/**
|
|
92
|
-
* Starts the proxy
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* result to a `StdioServerTransport`. Use `createProxyServer` directly when
|
|
96
|
-
* you need a testable handle to the configured server.
|
|
97
|
-
*
|
|
98
|
-
* @param upstream - Connected upstream MCP client.
|
|
99
|
-
* @param options - Optional middleware stacks for tools and resources.
|
|
77
|
+
* Starts the proxy on stdio.
|
|
78
|
+
* Calls {@link createProxyServer} then connects to `StdioServerTransport`.
|
|
79
|
+
* Use `createProxyServer` directly for testable access to the server.
|
|
100
80
|
*/
|
|
101
81
|
export async function startProxy(backend, options = {}) {
|
|
102
82
|
const server = createProxyServer(backend, options);
|
|
103
83
|
await server.connect(new StdioServerTransport());
|
|
104
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Starts the proxy over Streamable HTTP with stateful sessions.
|
|
87
|
+
*
|
|
88
|
+
* Sessions keyed by `mcp-session-id`. Upstream notifications fanned out to
|
|
89
|
+
* all active sessions via their GET SSE stream.
|
|
90
|
+
*
|
|
91
|
+
* **Limitations:**
|
|
92
|
+
* - Two calls with same `backend` overwrite notification handlers (last wins).
|
|
93
|
+
* - Sessions never expire — only cleaned up on DELETE or server close.
|
|
94
|
+
* - No `EventStore` → SSE reconnect replay unsupported.
|
|
95
|
+
*
|
|
96
|
+
* @returns Promise resolving to the listening `http.Server`.
|
|
97
|
+
*/
|
|
98
|
+
export function startHttpProxy(backend, options = {}, httpOptions = {}) {
|
|
99
|
+
const mcpPath = httpOptions.path ?? '/mcp';
|
|
100
|
+
const port = httpOptions.port ?? 3000;
|
|
101
|
+
const host = httpOptions.host;
|
|
102
|
+
// session ID → { transport, proxyServer }
|
|
103
|
+
const sessions = new Map();
|
|
104
|
+
// Fan upstream notifications to all active sessions' SSE streams.
|
|
105
|
+
backend.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
|
106
|
+
await Promise.all([...sessions.values()].map(({ proxyServer }) => proxyServer.sendToolListChanged()));
|
|
107
|
+
});
|
|
108
|
+
backend.setNotificationHandler(PromptListChangedNotificationSchema, async () => {
|
|
109
|
+
await Promise.all([...sessions.values()].map(({ proxyServer }) => proxyServer.sendPromptListChanged()));
|
|
110
|
+
});
|
|
111
|
+
backend.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
|
|
112
|
+
await Promise.all([...sessions.values()].map(({ proxyServer }) => proxyServer.sendResourceListChanged()));
|
|
113
|
+
});
|
|
114
|
+
const server = http.createServer((req, res) => {
|
|
115
|
+
const handle = async () => {
|
|
116
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
117
|
+
const method = req.method ?? '';
|
|
118
|
+
if (url.pathname !== mcpPath || !['GET', 'POST', 'DELETE'].includes(method)) {
|
|
119
|
+
res.writeHead(404).end();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
123
|
+
if (typeof sessionId === 'string') {
|
|
124
|
+
// Route to existing session
|
|
125
|
+
const session = sessions.get(sessionId);
|
|
126
|
+
if (!session) {
|
|
127
|
+
res.writeHead(404).end();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
await session.transport.handleRequest(req, res);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// New session (initialize request)
|
|
134
|
+
const proxyServer = createProxyServer(backend, options);
|
|
135
|
+
const transport = new StreamableHTTPServerTransport({
|
|
136
|
+
sessionIdGenerator: randomUUID,
|
|
137
|
+
onsessioninitialized: (id) => { sessions.set(id, { transport, proxyServer }); },
|
|
138
|
+
onsessionclosed: (id) => { sessions.delete(id); },
|
|
139
|
+
});
|
|
140
|
+
await proxyServer.connect(transport);
|
|
141
|
+
await transport.handleRequest(req, res);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
handle().catch((err) => {
|
|
145
|
+
if (!res.headersSent)
|
|
146
|
+
res.writeHead(500).end();
|
|
147
|
+
void err;
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
server.once('error', reject);
|
|
152
|
+
server.listen(port, ...(host ? [host] : []), () => {
|
|
153
|
+
server.off('error', reject);
|
|
154
|
+
resolve(server);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
105
158
|
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export type { Middleware } from './middleware.js';
|
|
|
2
2
|
export { compose } from './middleware.js';
|
|
3
3
|
export type { BackendConfig, BackendClient } from './backendClient.js';
|
|
4
4
|
export { createBackendClient } from './backendClient.js';
|
|
5
|
-
export type { ProxyOptions, ToolMiddleware, ResourceMiddleware, } from './core.js';
|
|
6
|
-
export { hasToolContent, createProxyServer, startProxy } from './core.js';
|
|
5
|
+
export type { ProxyOptions, HttpProxyOptions, ToolMiddleware, ResourceMiddleware, } from './core.js';
|
|
6
|
+
export { hasToolContent, createProxyServer, startProxy, startHttpProxy } from './core.js';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,YAAY,EACV,YAAY,EACZ,cAAc,EACd,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { compose } from './middleware.js';
|
|
2
2
|
export { createBackendClient } from './backendClient.js';
|
|
3
|
-
export { hasToolContent, createProxyServer, startProxy } from './core.js';
|
|
3
|
+
export { hasToolContent, createProxyServer, startProxy, startHttpProxy } from './core.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAQzD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,62 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Koa-style middleware composition for MCP request/response pipelines.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* `(req, next) => Promise<Res>` — call `next(req)` to delegate downstream.
|
|
5
|
+
* Middlewares wrap like an onion: outer runs before+after inner.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* The innermost `next` is the actual upstream call. Middlewares wrap it like
|
|
9
|
-
* an onion: outer layers execute code before AND after inner layers.
|
|
10
|
-
*
|
|
11
|
-
* Execution order with `pipe([piiMW, auditMW])` (or equivalently `compose([auditMW, piiMW])`):
|
|
7
|
+
* `pipe([piiMW, auditMW])` execution order:
|
|
12
8
|
* 1. auditMW enter → capture startTime
|
|
13
9
|
* 2. piiMW enter → call next
|
|
14
10
|
* 3. upstream call → raw response
|
|
15
11
|
* 4. piiMW exit → redact PII
|
|
16
|
-
* 5. auditMW exit → log clean result
|
|
17
|
-
*
|
|
18
|
-
* @module
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* A single middleware unit. Receives a request and a `next` function to
|
|
22
|
-
* call the remainder of the pipeline. May transform the request before
|
|
23
|
-
* calling `next` or transform the response after.
|
|
24
|
-
*
|
|
25
|
-
* @typeParam Req - Request type (e.g., `CallToolRequest`)
|
|
26
|
-
* @typeParam Res - Response type (e.g., `CallToolResult`)
|
|
12
|
+
* 5. auditMW exit → log clean result
|
|
27
13
|
*/
|
|
14
|
+
/** Single middleware unit. May transform req before `next` or res after. */
|
|
28
15
|
export type Middleware<Req, Res> = (req: Req, next: (req: Req) => Promise<Res>) => Promise<Res>;
|
|
29
16
|
/**
|
|
30
|
-
* Composes
|
|
31
|
-
* onion (Koa-style) model. The first middleware is the outermost layer.
|
|
32
|
-
*
|
|
33
|
-
* The returned function accepts the initial request and an innermost `next`
|
|
34
|
-
* (typically the upstream I/O call).
|
|
35
|
-
*
|
|
36
|
-
* @param middlewares - Ordered list of middlewares, outermost first.
|
|
37
|
-
* @returns A single composed middleware.
|
|
17
|
+
* Composes middlewares into one, outermost-first (Koa-style).
|
|
38
18
|
*
|
|
39
19
|
* @example
|
|
40
|
-
* ```ts
|
|
41
20
|
* const pipeline = compose([auditMW, piiMW]);
|
|
42
21
|
* const result = await pipeline(req, (r) => upstream.callTool(r.params));
|
|
43
|
-
* ```
|
|
44
22
|
*/
|
|
45
23
|
export declare function compose<Req, Res>(middlewares: ReadonlyArray<Middleware<Req, Res>>): Middleware<Req, Res>;
|
|
46
24
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* `pipe([piiMW, auditMW])` expresses "pii redacts first, then audit logs the clean result",
|
|
51
|
-
* which is equivalent to `compose([auditMW, piiMW])` (outermost-first).
|
|
52
|
-
*
|
|
53
|
-
* This is an internal utility. Consumers pass plain arrays to `ProxyOptions` which
|
|
54
|
-
* calls `pipe()` internally — there is no need to call `pipe()` directly.
|
|
55
|
-
*
|
|
56
|
-
* Equivalent to `compose([...middlewares].reverse())`.
|
|
57
|
-
*
|
|
58
|
-
* @param middlewares - Middlewares in response-processing order (first processes response first).
|
|
59
|
-
* @returns A single composed middleware.
|
|
25
|
+
* Like `compose` but in response-processing order (first = innermost).
|
|
26
|
+
* `pipe([piiMW, auditMW])` ≡ `compose([auditMW, piiMW])`.
|
|
27
|
+
* Used internally by mcpose core — consumers pass arrays to `ProxyOptions`.
|
|
60
28
|
*/
|
|
61
29
|
export declare function pipe<Req, Res>(middlewares: ReadonlyArray<Middleware<Req, Res>>): Middleware<Req, Res>;
|
|
62
30
|
//# sourceMappingURL=middleware.d.ts.map
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,CACjC,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,KAC7B,OAAO,CAAC,GAAG,CAAC,CAAC;AAElB;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAC9B,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAC/C,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAkBtB;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,GAAG,EAC3B,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAC/C,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAEtB"}
|
package/dist/middleware.js
CHANGED
|
@@ -1,37 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Koa-style middleware composition for MCP request/response pipelines.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* `(req, next) => Promise<Res>` — call `next(req)` to delegate downstream.
|
|
5
|
+
* Middlewares wrap like an onion: outer runs before+after inner.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* The innermost `next` is the actual upstream call. Middlewares wrap it like
|
|
9
|
-
* an onion: outer layers execute code before AND after inner layers.
|
|
10
|
-
*
|
|
11
|
-
* Execution order with `pipe([piiMW, auditMW])` (or equivalently `compose([auditMW, piiMW])`):
|
|
7
|
+
* `pipe([piiMW, auditMW])` execution order:
|
|
12
8
|
* 1. auditMW enter → capture startTime
|
|
13
9
|
* 2. piiMW enter → call next
|
|
14
10
|
* 3. upstream call → raw response
|
|
15
11
|
* 4. piiMW exit → redact PII
|
|
16
|
-
* 5. auditMW exit → log clean result
|
|
17
|
-
*
|
|
18
|
-
* @module
|
|
12
|
+
* 5. auditMW exit → log clean result
|
|
19
13
|
*/
|
|
20
14
|
/**
|
|
21
|
-
* Composes
|
|
22
|
-
* onion (Koa-style) model. The first middleware is the outermost layer.
|
|
23
|
-
*
|
|
24
|
-
* The returned function accepts the initial request and an innermost `next`
|
|
25
|
-
* (typically the upstream I/O call).
|
|
26
|
-
*
|
|
27
|
-
* @param middlewares - Ordered list of middlewares, outermost first.
|
|
28
|
-
* @returns A single composed middleware.
|
|
15
|
+
* Composes middlewares into one, outermost-first (Koa-style).
|
|
29
16
|
*
|
|
30
17
|
* @example
|
|
31
|
-
* ```ts
|
|
32
18
|
* const pipeline = compose([auditMW, piiMW]);
|
|
33
19
|
* const result = await pipeline(req, (r) => upstream.callTool(r.params));
|
|
34
|
-
* ```
|
|
35
20
|
*/
|
|
36
21
|
export function compose(middlewares) {
|
|
37
22
|
return (req, next) => {
|
|
@@ -48,19 +33,9 @@ export function compose(middlewares) {
|
|
|
48
33
|
};
|
|
49
34
|
}
|
|
50
35
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* `pipe([piiMW, auditMW])` expresses "pii redacts first, then audit logs the clean result",
|
|
55
|
-
* which is equivalent to `compose([auditMW, piiMW])` (outermost-first).
|
|
56
|
-
*
|
|
57
|
-
* This is an internal utility. Consumers pass plain arrays to `ProxyOptions` which
|
|
58
|
-
* calls `pipe()` internally — there is no need to call `pipe()` directly.
|
|
59
|
-
*
|
|
60
|
-
* Equivalent to `compose([...middlewares].reverse())`.
|
|
61
|
-
*
|
|
62
|
-
* @param middlewares - Middlewares in response-processing order (first processes response first).
|
|
63
|
-
* @returns A single composed middleware.
|
|
36
|
+
* Like `compose` but in response-processing order (first = innermost).
|
|
37
|
+
* `pipe([piiMW, auditMW])` ≡ `compose([auditMW, piiMW])`.
|
|
38
|
+
* Used internally by mcpose core — consumers pass arrays to `ProxyOptions`.
|
|
64
39
|
*/
|
|
65
40
|
export function pipe(middlewares) {
|
|
66
41
|
return compose([...middlewares].reverse());
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CACrB,WAAgD;IAEhD,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;QACnB,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;QAEf,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,UAAe,EAAgB,EAAE;YAC5D,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;gBACf,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;YACnE,CAAC;YACD,KAAK,GAAG,CAAC,CAAC;YAEV,MAAM,EAAE,GACN,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAEjE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC,CAAC;QAEF,OAAO,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAClB,WAAgD;IAEhD,OAAO,OAAO,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAC7C,CAAC"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,78 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* **No test framework imports** — this module has zero dependencies on
|
|
5
|
-
* vitest, jest, or mocha. Import it in any test environment.
|
|
6
|
-
*
|
|
2
|
+
* Test helpers for mcpose middleware. No test framework imports — works in any env.
|
|
7
3
|
* @module mcpose/testing
|
|
8
4
|
*/
|
|
9
5
|
import type { CallToolRequest, CallToolResult, ReadResourceResult, Tool, Resource, Prompt, CallToolRequestParams, GetPromptResult } from '@modelcontextprotocol/sdk/types.js';
|
|
10
6
|
import type { ToolMiddleware } from './core.js';
|
|
11
7
|
import type { BackendClient } from './backendClient.js';
|
|
12
8
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Eliminates the boilerplate `isCallToolResult` guard that every test of a
|
|
16
|
-
* `ToolMiddleware` must otherwise repeat. Throws a clear error if the upstream
|
|
17
|
-
* returns the legacy `{ toolResult }` protocol shape.
|
|
18
|
-
*
|
|
19
|
-
* @param mw - The middleware under test.
|
|
20
|
-
* @param req - The tool call request to pass in.
|
|
21
|
-
* @param next - The innermost handler (simulates the upstream or next middleware).
|
|
9
|
+
* Runs a `ToolMiddleware` and narrows result to `CallToolResult`.
|
|
10
|
+
* Throws if upstream returns legacy `{ toolResult }` shape.
|
|
22
11
|
*
|
|
23
12
|
* @example
|
|
24
|
-
* ```ts
|
|
25
13
|
* const result = await runToolMiddleware(mw, req, async () => mockResult);
|
|
26
14
|
* expect(result.content[0]).toMatchObject({ type: 'text' });
|
|
27
|
-
* ```
|
|
28
15
|
*/
|
|
29
16
|
export declare function runToolMiddleware(mw: ToolMiddleware, req: CallToolRequest, next: (req: CallToolRequest) => Promise<CallToolResult>): Promise<CallToolResult>;
|
|
30
|
-
/**
|
|
31
|
-
* Configuration for the mock backend client.
|
|
32
|
-
*/
|
|
17
|
+
/** Config for mock backend client. */
|
|
33
18
|
export interface MockBackendClientOptions {
|
|
34
|
-
/**
|
|
19
|
+
/** Default: `[]` */
|
|
35
20
|
tools?: Tool[];
|
|
36
21
|
/**
|
|
37
|
-
*
|
|
38
|
-
* that receives the call params and returns a per-call result.
|
|
39
|
-
*
|
|
22
|
+
* Static result or factory `(params) => result` for `callTool`.
|
|
40
23
|
* Default: `{ content: [{ type: 'text', text: 'mock response' }] }`
|
|
41
24
|
*/
|
|
42
25
|
callToolResponse?: CallToolResult | ((params: CallToolRequestParams) => CallToolResult);
|
|
43
|
-
/**
|
|
26
|
+
/** Default: `[]` */
|
|
44
27
|
resources?: Resource[];
|
|
45
|
-
/**
|
|
46
|
-
* Response for `readResource`.
|
|
47
|
-
*
|
|
48
|
-
* Default: `{ contents: [{ uri: '', text: 'mock resource' }] }`
|
|
49
|
-
*/
|
|
28
|
+
/** Default: `{ contents: [{ uri: '', text: 'mock resource' }] }` */
|
|
50
29
|
readResourceResponse?: ReadResourceResult;
|
|
51
|
-
/**
|
|
30
|
+
/** Default: `[]` */
|
|
52
31
|
prompts?: Prompt[];
|
|
53
|
-
/**
|
|
54
|
-
* Response for `getPrompt`.
|
|
55
|
-
*
|
|
56
|
-
* Default: `{ messages: [] }`
|
|
57
|
-
*/
|
|
32
|
+
/** Default: `{ messages: [] }` */
|
|
58
33
|
getPromptResponse?: GetPromptResult;
|
|
59
34
|
}
|
|
60
35
|
/**
|
|
61
|
-
* Creates
|
|
62
|
-
*
|
|
63
|
-
* No real process is spawned and no network connection is made. All methods
|
|
64
|
-
* return configurable in-memory responses, making it possible to test the full
|
|
65
|
-
* `compose([auditMW, piiMW])` pipeline in a unit test.
|
|
36
|
+
* Creates an in-memory `BackendClient` for unit tests. No process/network.
|
|
66
37
|
*
|
|
67
38
|
* @example
|
|
68
|
-
* ```ts
|
|
69
39
|
* const backend = createMockBackendClient({
|
|
70
40
|
* callToolResponse: { content: [{ type: 'text', text: 'John Doe: 123-45-6789' }] },
|
|
71
41
|
* });
|
|
72
|
-
* // Now compose real middlewares against it:
|
|
73
42
|
* const pipeline = compose([auditMW, piiToolMW]);
|
|
74
43
|
* const result = await pipeline(req, (r) => backend.callTool(r.params, undefined));
|
|
75
|
-
* ```
|
|
76
44
|
*/
|
|
77
45
|
export declare function createMockBackendClient(options?: MockBackendClientOptions): BackendClient;
|
|
78
46
|
//# sourceMappingURL=testing.d.ts.map
|
package/dist/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAId,kBAAkB,EAClB,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,qBAAqB,EAGrB,eAAe,EAChB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,cAAc,EAClB,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,cAAc,CAAC,GACtD,OAAO,CAAC,cAAc,CAAC,CAQzB;AAED,sCAAsC;AACtC,MAAM,WAAW,wBAAwB;IACvC,oBAAoB;IACpB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf;;;OAGG;IACH,gBAAgB,CAAC,EACb,cAAc,GACd,CAAC,CAAC,MAAM,EAAE,qBAAqB,KAAK,cAAc,CAAC,CAAC;IACxD,oBAAoB;IACpB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,oEAAoE;IACpE,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kCAAkC;IAClC,iBAAiB,CAAC,EAAE,eAAe,CAAC;CACrC;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,aAAa,CAkCf"}
|
package/dist/testing.js
CHANGED
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
import { hasToolContent } from './core.js';
|
|
2
|
-
// ---------------------------------------------------------------------------
|
|
3
|
-
// Pain Point 1: runToolMiddleware
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
2
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Eliminates the boilerplate `isCallToolResult` guard that every test of a
|
|
9
|
-
* `ToolMiddleware` must otherwise repeat. Throws a clear error if the upstream
|
|
10
|
-
* returns the legacy `{ toolResult }` protocol shape.
|
|
11
|
-
*
|
|
12
|
-
* @param mw - The middleware under test.
|
|
13
|
-
* @param req - The tool call request to pass in.
|
|
14
|
-
* @param next - The innermost handler (simulates the upstream or next middleware).
|
|
3
|
+
* Runs a `ToolMiddleware` and narrows result to `CallToolResult`.
|
|
4
|
+
* Throws if upstream returns legacy `{ toolResult }` shape.
|
|
15
5
|
*
|
|
16
6
|
* @example
|
|
17
|
-
* ```ts
|
|
18
7
|
* const result = await runToolMiddleware(mw, req, async () => mockResult);
|
|
19
8
|
* expect(result.content[0]).toMatchObject({ type: 'text' });
|
|
20
|
-
* ```
|
|
21
9
|
*/
|
|
22
10
|
export async function runToolMiddleware(mw, req, next) {
|
|
23
11
|
const result = await mw(req, next);
|
|
@@ -27,21 +15,14 @@ export async function runToolMiddleware(mw, req, next) {
|
|
|
27
15
|
return result;
|
|
28
16
|
}
|
|
29
17
|
/**
|
|
30
|
-
* Creates
|
|
31
|
-
*
|
|
32
|
-
* No real process is spawned and no network connection is made. All methods
|
|
33
|
-
* return configurable in-memory responses, making it possible to test the full
|
|
34
|
-
* `compose([auditMW, piiMW])` pipeline in a unit test.
|
|
18
|
+
* Creates an in-memory `BackendClient` for unit tests. No process/network.
|
|
35
19
|
*
|
|
36
20
|
* @example
|
|
37
|
-
* ```ts
|
|
38
21
|
* const backend = createMockBackendClient({
|
|
39
22
|
* callToolResponse: { content: [{ type: 'text', text: 'John Doe: 123-45-6789' }] },
|
|
40
23
|
* });
|
|
41
|
-
* // Now compose real middlewares against it:
|
|
42
24
|
* const pipeline = compose([auditMW, piiToolMW]);
|
|
43
25
|
* const result = await pipeline(req, (r) => backend.callTool(r.params, undefined));
|
|
44
|
-
* ```
|
|
45
26
|
*/
|
|
46
27
|
export function createMockBackendClient(options = {}) {
|
|
47
28
|
return {
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAI3C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAkB,EAClB,GAAoB,EACpB,IAAuD;IAEvD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAuBD;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CACrC,UAAoC,EAAE;IAEtC,OAAO;QACL,SAAS,EAAE,KAAK,IAA8B,EAAE,CAAC,CAAC;YAChD,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;SAC3B,CAAC;QAEF,QAAQ,EAAE,KAAK,EACb,MAA6B,EACJ,EAAE;YAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;YACtC,IAAI,OAAO,IAAI,KAAK,UAAU;gBAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;YACpD,OAAO,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC;QACxE,CAAC;QAED,aAAa,EAAE,KAAK,IAAkC,EAAE,CAAC,CAAC;YACxD,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;SACnC,CAAC;QAEF,YAAY,EAAE,KAAK,EACjB,OAAkC,EACL,EAAE,CAC/B,OAAO,CAAC,oBAAoB,IAAI;YAC9B,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;SAC/C;QAEH,WAAW,EAAE,KAAK,IAAgC,EAAE,CAAC,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;SAC/B,CAAC;QAEF,SAAS,EAAE,KAAK,EACd,OAA+B,EACL,EAAE,CAC5B,OAAO,CAAC,iBAAiB,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;KACpB,CAAC;AAChC,CAAC"}
|