mcpose 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 +21 -0
- package/README.md +249 -0
- package/dist/backendClient.d.ts +34 -0
- package/dist/backendClient.d.ts.map +1 -0
- package/dist/backendClient.js +39 -0
- package/dist/backendClient.js.map +1 -0
- package/dist/core.d.ts +85 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +105 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +62 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +68 -0
- package/dist/middleware.js.map +1 -0
- package/dist/testing.d.ts +78 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +69 -0
- package/dist/testing.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Amir Gorji
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# mcpose
|
|
2
|
+
|
|
3
|
+
Composable middleware proxy for MCP servers.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Background
|
|
8
|
+
|
|
9
|
+
mcpose was extracted from [`financial-elastic-mcp-server`](https://github.com/amir-gorji/financial-elastic-mcp-server), an Elasticsearch MCP server built for financial institutions that needed PII redaction and audit logging on every tool call. Those cross-cutting concerns were originally hardcoded into a single server. mcpose lifts that pattern into a reusable, composable middleware layer that can wrap **any** upstream MCP server.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Concept
|
|
14
|
+
|
|
15
|
+
mcpose is a **transparent proxy**: it sits between an LLM client and an upstream MCP server, mirroring the upstream's tool, resource, and prompt lists while routing all calls through a configurable middleware pipeline. The client sees a normal MCP server; the upstream sees a normal MCP client. mcpose is the layer in between — controlling visibility and applying transformations without either side knowing.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install mcpose
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Peer dependency** — must be installed separately:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @modelcontextprotocol/sdk@>=1.0.0
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { createBackendClient, startProxy } from 'mcpose';
|
|
37
|
+
import type { ToolMiddleware } from 'mcpose';
|
|
38
|
+
|
|
39
|
+
// 1. Connect to the upstream MCP server (stdio)
|
|
40
|
+
const backend = await createBackendClient({
|
|
41
|
+
command: 'node',
|
|
42
|
+
args: ['/path/to/backend-server.mjs'],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// 2. Define middleware
|
|
46
|
+
const loggingMW: ToolMiddleware = async (req, next) => {
|
|
47
|
+
console.error(`→ ${req.params.name}`);
|
|
48
|
+
const result = await next(req);
|
|
49
|
+
console.error(`← ${req.params.name} done`);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// 3. Start the proxy on stdio
|
|
54
|
+
await startProxy(backend, {
|
|
55
|
+
toolMiddleware: [loggingMW],
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Proxy model
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
┌──────────────┐ ┌────────────────────────────────┐ ┌────────────────────┐
|
|
65
|
+
│ LLM client │ ◄────► │ mcpose │ ◄────► │ Upstream MCP │
|
|
66
|
+
│ (Claude, │ │ · visibility filters │ │ server │
|
|
67
|
+
│ Cursor…) │ │ · middleware pipelines │ │ (stdio or HTTP) │
|
|
68
|
+
└──────────────┘ └────────────────────────────────┘ └────────────────────┘
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For each tool or resource, mcpose picks one of three routing paths:
|
|
72
|
+
|
|
73
|
+
| Path | Option | Behavior |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| **Hidden** | `hiddenTools` / `hiddenResources` | Omitted from list responses; rejected with an error at call time |
|
|
76
|
+
| **Pass-through** | `passThroughTools` / `passThroughResources` | Forwarded raw to upstream — all middleware skipped |
|
|
77
|
+
| **Middleware** | everything else | Routed through the full `toolMiddleware` / `resourceMiddleware` pipeline |
|
|
78
|
+
|
|
79
|
+
Prompts are always forwarded as-is — no filtering or middleware.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Middleware model
|
|
84
|
+
|
|
85
|
+
Middleware follows the **onion model**: outer layers run code before *and* after inner layers. Each middleware receives the request and a `next` function to invoke the rest of the pipeline.
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
request ──►
|
|
89
|
+
┌──────────────────────────────────────────┐
|
|
90
|
+
│ outerMW (enter) │
|
|
91
|
+
│ ┌────────────────────────────────────┐ │
|
|
92
|
+
│ │ innerMW (enter) │ │
|
|
93
|
+
│ │ ┌──────────────────────────────┐ │ │
|
|
94
|
+
│ │ │ upstream call │ │ │
|
|
95
|
+
│ │ └──────────────────────────────┘ │ │
|
|
96
|
+
│ │ innerMW (exit) ◄── response │ │
|
|
97
|
+
│ └────────────────────────────────────┘ │
|
|
98
|
+
│ outerMW (exit) ◄── response │
|
|
99
|
+
└──────────────────────────────────────────┘
|
|
100
|
+
◄── response
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Array order in `ProxyOptions`** uses **response-processing order**: the first element processes the response *first* (innermost layer). `ProxyOptions` calls `pipe()` internally — no need to wrap manually. To guarantee audit never sees raw PII:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
toolMiddleware: [piiMW, auditMW]
|
|
107
|
+
// Execution:
|
|
108
|
+
// 1. auditMW enter → capture startTime (outermost)
|
|
109
|
+
// 2. piiMW enter → transform request
|
|
110
|
+
// 3. upstream call
|
|
111
|
+
// 4. piiMW exit → redact PII from response (processes response first)
|
|
112
|
+
// 5. auditMW exit → log already-clean data (processes response last)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`compose([outerMW, innerMW])` uses the **opposite** (outermost-first) convention — `ProxyOptions` arrays are **not** interchangeable with `compose()` arguments.
|
|
116
|
+
|
|
117
|
+
A middleware can **short-circuit** by returning without calling `next`, or **handle upstream errors** by wrapping `await next(req)` in a try/catch.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## API Reference
|
|
122
|
+
|
|
123
|
+
### `Middleware<Req, Res>` · `ToolMiddleware` · `ResourceMiddleware` · `compose()`
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
type Middleware<Req, Res> = (
|
|
127
|
+
req: Req,
|
|
128
|
+
next: (req: Req) => Promise<Res>,
|
|
129
|
+
) => Promise<Res>;
|
|
130
|
+
|
|
131
|
+
// Convenience aliases for the two pipeline types:
|
|
132
|
+
type ToolMiddleware = Middleware<CallToolRequest, CompatibilityCallToolResult>;
|
|
133
|
+
type ResourceMiddleware = Middleware<ReadResourceRequest, ReadResourceResult>;
|
|
134
|
+
|
|
135
|
+
function compose<Req, Res>(
|
|
136
|
+
middlewares: ReadonlyArray<Middleware<Req, Res>>,
|
|
137
|
+
): Middleware<Req, Res>;
|
|
138
|
+
|
|
139
|
+
// Type guard — narrows CompatibilityCallToolResult to CallToolResult
|
|
140
|
+
// (safe access to .content and .isError without casts):
|
|
141
|
+
function hasToolContent(r: CompatibilityCallToolResult): r is CallToolResult;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`compose` takes an array in **outermost-first** order. Use `hasToolContent` in middleware implementations before accessing `.content` or `.isError`, since `CompatibilityCallToolResult` also covers the legacy `{ toolResult }` shape.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### `BackendConfig` · `createBackendClient()`
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
interface BackendConfig {
|
|
152
|
+
command?: string; // Executable to spawn for stdio transport (e.g., "node")
|
|
153
|
+
args?: string[]; // Arguments for the spawned process
|
|
154
|
+
url?: string; // HTTP endpoint of a running MCP server (takes precedence over stdio)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function createBackendClient(config: BackendConfig): Promise<BackendClient>;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`BackendClient` is an alias for the SDK `Client`. Throws if neither `command` nor `url` is provided, or if the connection fails.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### `ProxyOptions` · `startProxy()` · `createProxyServer()`
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
interface ProxyOptions {
|
|
168
|
+
toolMiddleware?: ReadonlyArray<ToolMiddleware>;
|
|
169
|
+
resourceMiddleware?: ReadonlyArray<ResourceMiddleware>;
|
|
170
|
+
passThroughTools?: ReadonlyArray<string>;
|
|
171
|
+
passThroughResources?: ReadonlyArray<string>;
|
|
172
|
+
hiddenTools?: ReadonlyArray<string>;
|
|
173
|
+
hiddenResources?: ReadonlyArray<string>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function startProxy(backend: BackendClient, options?: ProxyOptions): Promise<void>;
|
|
177
|
+
function createProxyServer(backend: BackendClient, options?: ProxyOptions): Server;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
| Option | Description |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `toolMiddleware` | Middleware stack for tool calls, in response-processing order (first element processes response first). |
|
|
183
|
+
| `resourceMiddleware` | Middleware stack for resource reads, in response-processing order. |
|
|
184
|
+
| `passThroughTools` | Tool names forwarded raw to upstream — middleware skipped entirely. |
|
|
185
|
+
| `passThroughResources` | Resource URIs forwarded raw to upstream — middleware skipped entirely. |
|
|
186
|
+
| `hiddenTools` | Tool names removed from `list_tools` **and** rejected at call time with `MethodNotFound`. |
|
|
187
|
+
| `hiddenResources` | Resource URIs removed from `list_resources` **and** rejected at call time with `InvalidRequest`. |
|
|
188
|
+
|
|
189
|
+
`startProxy` connects the proxy to a `StdioServerTransport`. `createProxyServer` returns the configured `Server` without connecting — useful for testing request handlers without a live transport.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Recipe: PII redaction
|
|
194
|
+
|
|
195
|
+
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.
|
|
196
|
+
|
|
197
|
+
Use a factory to keep middleware configurable and testable:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import { hasToolContent } from 'mcpose';
|
|
201
|
+
import type { ToolMiddleware } from 'mcpose';
|
|
202
|
+
|
|
203
|
+
function createPiiMiddleware(patterns: RegExp[]): ToolMiddleware {
|
|
204
|
+
return async (req, next) => {
|
|
205
|
+
const result = await next(req);
|
|
206
|
+
if (!hasToolContent(result)) return result;
|
|
207
|
+
return {
|
|
208
|
+
...result,
|
|
209
|
+
content: result.content.map((item) =>
|
|
210
|
+
item.type === 'text'
|
|
211
|
+
? { ...item, text: redactPii(item.text, patterns) }
|
|
212
|
+
: item,
|
|
213
|
+
),
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function redactPii(text: string, patterns: RegExp[]): string {
|
|
219
|
+
return patterns.reduce((t, re) => t.replace(re, '[REDACTED]'), text);
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Stack it with audit middleware — PII first in the array so audit always sees clean data:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
await startProxy(backend, {
|
|
227
|
+
toolMiddleware: [
|
|
228
|
+
createPiiMiddleware([/\b\d{9}\b/g, /[A-Z]{2}\d{6}/g]), // SSNs, account numbers
|
|
229
|
+
createAuditMiddleware({ destination: auditLog }),
|
|
230
|
+
],
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The array order guarantees: PII is redacted *before* the audit layer ever sees the response. No raw PII reaches a log, satisfying financial regulatory requirements.
|
|
235
|
+
|
|
236
|
+
> **Reference implementation:** [`elastic-pii-proxy`](https://github.com/amir-gorji/elastic-pii-proxy) is a production example of this pattern — an Elasticsearch MCP proxy that uses mcpose with a PII redaction middleware and an audit middleware to serve financial data safely to LLM agents.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Roadmap
|
|
241
|
+
|
|
242
|
+
- [ ] **HTTP/SSE server transport** — currently the proxy emits only a stdio server; add a streamable HTTP/SSE server-side transport so mcpose can front HTTP-based clients without a subprocess wrapper
|
|
243
|
+
- [ ] **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
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for connecting to the backend MCP server.
|
|
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
|
|
14
|
+
*/
|
|
15
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
16
|
+
/** Connection options for the backend MCP server. */
|
|
17
|
+
export interface BackendConfig {
|
|
18
|
+
/** Shell command to spawn the backend server (e.g., `"node"`). Required when not using `url`. */
|
|
19
|
+
command?: string;
|
|
20
|
+
/** Arguments passed to the command (e.g., `["/path/to/server.mjs"]`). */
|
|
21
|
+
args?: string[];
|
|
22
|
+
/** HTTP/SSE URL of an already-running backend server. Takes precedence over stdio when set. */
|
|
23
|
+
url?: string;
|
|
24
|
+
}
|
|
25
|
+
export type BackendClient = Client;
|
|
26
|
+
/**
|
|
27
|
+
* Creates and connects an MCP client to the backend server.
|
|
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.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createBackendClient(config: BackendConfig): Promise<BackendClient>;
|
|
34
|
+
//# sourceMappingURL=backendClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backendClient.d.ts","sourceRoot":"","sources":["../src/backendClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,+FAA+F;IAC/F,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,aAAa,CAAC,CAqBxB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for connecting to the backend MCP server.
|
|
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
|
|
14
|
+
*/
|
|
15
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
16
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
17
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
18
|
+
/**
|
|
19
|
+
* Creates and connects an MCP client to the backend server.
|
|
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.
|
|
24
|
+
*/
|
|
25
|
+
export async function createBackendClient(config) {
|
|
26
|
+
if (!config.command && !config.url) {
|
|
27
|
+
throw new Error('mcpose: either command or url must be provided in BackendConfig');
|
|
28
|
+
}
|
|
29
|
+
const client = new Client({ name: 'mcpose-backend', version: '1.0.0' }, { capabilities: {} });
|
|
30
|
+
const transport = config.url
|
|
31
|
+
? new StreamableHTTPClientTransport(new URL(config.url))
|
|
32
|
+
: new StdioClientTransport({
|
|
33
|
+
command: config.command,
|
|
34
|
+
args: config.args ?? [],
|
|
35
|
+
});
|
|
36
|
+
await client.connect(transport);
|
|
37
|
+
return client;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=backendClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backendClient.js","sourceRoot":"","sources":["../src/backendClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;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;;;;;;GAMG;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
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic MCP proxy core.
|
|
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
|
+
*/
|
|
9
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
|
+
import { type CallToolRequest, type CallToolResult, type CompatibilityCallToolResult, type ReadResourceRequest, type ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
|
|
11
|
+
import { type Middleware } from './middleware.js';
|
|
12
|
+
import type { BackendClient } from './backendClient.js';
|
|
13
|
+
/**
|
|
14
|
+
* Middleware for MCP tool calls.
|
|
15
|
+
*
|
|
16
|
+
* Uses `CompatibilityCallToolResult` because `Client.callTool()` returns a
|
|
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`.
|
|
20
|
+
*/
|
|
21
|
+
export type ToolMiddleware = Middleware<CallToolRequest, CompatibilityCallToolResult>;
|
|
22
|
+
/** Middleware for MCP resource reads. */
|
|
23
|
+
export type ResourceMiddleware = Middleware<ReadResourceRequest, ReadResourceResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Type guard that narrows a {@link CompatibilityCallToolResult} to the modern
|
|
26
|
+
* {@link CallToolResult} shape (i.e. the result has a `content` array).
|
|
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`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function hasToolContent(r: CompatibilityCallToolResult): r is CallToolResult;
|
|
33
|
+
/** Options for the proxy server. */
|
|
34
|
+
export interface ProxyOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Ordered middleware stack for tool calls in response-processing order.
|
|
37
|
+
* The first element processes the response first (innermost layer).
|
|
38
|
+
* `pipe()` is called internally — no need to wrap manually.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* toolMiddleware: [piiMW, auditMW] // pii redacts first, audit logs clean data
|
|
42
|
+
*/
|
|
43
|
+
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
|
+
*/
|
|
48
|
+
resourceMiddleware?: ReadonlyArray<ResourceMiddleware>;
|
|
49
|
+
/** Tool names that bypass all middleware — raw upstream response forwarded as-is. */
|
|
50
|
+
passThroughTools?: ReadonlyArray<string>;
|
|
51
|
+
/** Resource URIs that bypass all middleware — raw upstream response forwarded as-is. */
|
|
52
|
+
passThroughResources?: ReadonlyArray<string>;
|
|
53
|
+
/** Tool names hidden from list_tools AND rejected at runtime with MethodNotFound. */
|
|
54
|
+
hiddenTools?: ReadonlyArray<string>;
|
|
55
|
+
/** Resource URIs hidden from list_resources AND rejected at runtime with InvalidRequest. */
|
|
56
|
+
hiddenResources?: ReadonlyArray<string>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates and wires a proxy MCP server without connecting it to a transport.
|
|
60
|
+
*
|
|
61
|
+
* Mirrors the upstream's tool/resource/prompt list and registers request
|
|
62
|
+
* handlers that route through the provided middleware pipelines. Prompts are
|
|
63
|
+
* forwarded as-is (no middleware applied).
|
|
64
|
+
*
|
|
65
|
+
* Separating creation from connection makes the server fully testable: tests
|
|
66
|
+
* can call `createProxyServer(mockUpstream, options)` and inspect or invoke
|
|
67
|
+
* the registered handlers without spawning a stdio transport.
|
|
68
|
+
*
|
|
69
|
+
* @param upstream - Connected (or mock) upstream MCP client.
|
|
70
|
+
* @param options - Optional middleware stacks for tools and resources.
|
|
71
|
+
* @returns A configured {@link Server} that is ready to be connected.
|
|
72
|
+
*/
|
|
73
|
+
export declare function createProxyServer(backend: BackendClient, options?: ProxyOptions): Server;
|
|
74
|
+
/**
|
|
75
|
+
* Starts the proxy MCP server on stdio.
|
|
76
|
+
*
|
|
77
|
+
* Convenience wrapper that calls {@link createProxyServer} then connects the
|
|
78
|
+
* result to a `StdioServerTransport`. Use `createProxyServer` directly when
|
|
79
|
+
* you need a testable handle to the configured server.
|
|
80
|
+
*
|
|
81
|
+
* @param upstream - Connected upstream MCP client.
|
|
82
|
+
* @param options - Optional middleware stacks for tools and resources.
|
|
83
|
+
*/
|
|
84
|
+
export declare function startProxy(backend: BackendClient, options?: ProxyOptions): Promise<void>;
|
|
85
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EASL,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;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,CACrC,eAAe,EACf,2BAA2B,CAC5B,CAAC;AAEF,yCAAyC;AACzC,MAAM,MAAM,kBAAkB,GAAG,UAAU,CACzC,mBAAmB,EACnB,kBAAkB,CACnB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,2BAA2B,GAC7B,CAAC,IAAI,cAAc,CAErB;AAED,oCAAoC;AACpC,MAAM,WAAW,YAAY;IAC3B;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAE/C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAEvD,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEzC,wFAAwF;IACxF,oBAAoB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAE7C,qFAAqF;IACrF,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEpC,4FAA4F;IAC5F,eAAe,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,YAAiB,GACzB,MAAM,CAoER;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic MCP proxy core.
|
|
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
|
+
*/
|
|
9
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
|
+
import { CallToolRequestSchema, ErrorCode, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
12
|
+
import { pipe } from './middleware.js';
|
|
13
|
+
/**
|
|
14
|
+
* Type guard that narrows a {@link CompatibilityCallToolResult} to the modern
|
|
15
|
+
* {@link CallToolResult} shape (i.e. the result has a `content` array).
|
|
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`.
|
|
20
|
+
*/
|
|
21
|
+
export function hasToolContent(r) {
|
|
22
|
+
return Array.isArray(r.content);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates and wires a proxy MCP server without connecting it to a transport.
|
|
26
|
+
*
|
|
27
|
+
* Mirrors the upstream's tool/resource/prompt list and registers request
|
|
28
|
+
* handlers that route through the provided middleware pipelines. Prompts are
|
|
29
|
+
* forwarded as-is (no middleware applied).
|
|
30
|
+
*
|
|
31
|
+
* Separating creation from connection makes the server fully testable: tests
|
|
32
|
+
* can call `createProxyServer(mockUpstream, options)` and inspect or invoke
|
|
33
|
+
* the registered handlers without spawning a stdio transport.
|
|
34
|
+
*
|
|
35
|
+
* @param upstream - Connected (or mock) upstream MCP client.
|
|
36
|
+
* @param options - Optional middleware stacks for tools and resources.
|
|
37
|
+
* @returns A configured {@link Server} that is ready to be connected.
|
|
38
|
+
*/
|
|
39
|
+
export function createProxyServer(backend, options = {}) {
|
|
40
|
+
const toolPipeline = pipe(options.toolMiddleware ?? []);
|
|
41
|
+
const resourcePipeline = pipe(options.resourceMiddleware ?? []);
|
|
42
|
+
const hiddenToolSet = new Set(options.hiddenTools ?? []);
|
|
43
|
+
const passThroughToolSet = new Set(options.passThroughTools ?? []);
|
|
44
|
+
const hiddenResourceSet = new Set(options.hiddenResources ?? []);
|
|
45
|
+
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
|
+
const server = new Server({ name: 'mcpose', version: '1.0.0' }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
52
|
+
// ── Tool handlers ──────────────────────────────────────────────────────────
|
|
53
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
54
|
+
const result = await backend.listTools();
|
|
55
|
+
if (!hiddenToolSet.size)
|
|
56
|
+
return result;
|
|
57
|
+
return { ...result, tools: result.tools.filter((t) => !hiddenToolSet.has(t.name)) };
|
|
58
|
+
});
|
|
59
|
+
server.setRequestHandler(CallToolRequestSchema, (req) => {
|
|
60
|
+
const name = req.params.name;
|
|
61
|
+
if (hiddenToolSet.has(name)) {
|
|
62
|
+
throw new McpError(ErrorCode.MethodNotFound, `Tool not found: ${name}`);
|
|
63
|
+
}
|
|
64
|
+
if (passThroughToolSet.has(name)) {
|
|
65
|
+
return backend.callTool(req.params, undefined);
|
|
66
|
+
}
|
|
67
|
+
return toolPipeline(req, (r) => backend.callTool(r.params, undefined));
|
|
68
|
+
});
|
|
69
|
+
// ── Resource handlers ──────────────────────────────────────────────────────
|
|
70
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
71
|
+
const result = await backend.listResources();
|
|
72
|
+
if (!hiddenResourceSet.size)
|
|
73
|
+
return result;
|
|
74
|
+
return { ...result, resources: result.resources.filter((r) => !hiddenResourceSet.has(r.uri)) };
|
|
75
|
+
});
|
|
76
|
+
server.setRequestHandler(ReadResourceRequestSchema, (req) => {
|
|
77
|
+
const uri = req.params.uri;
|
|
78
|
+
if (hiddenResourceSet.has(uri)) {
|
|
79
|
+
throw new McpError(ErrorCode.InvalidRequest, `Resource not found: ${uri}`);
|
|
80
|
+
}
|
|
81
|
+
if (passThroughResourceSet.has(uri)) {
|
|
82
|
+
return backend.readResource(req.params);
|
|
83
|
+
}
|
|
84
|
+
return resourcePipeline(req, (r) => backend.readResource(r.params));
|
|
85
|
+
});
|
|
86
|
+
// ── Prompt handlers (pass-through) ────────────────────────────────────────
|
|
87
|
+
server.setRequestHandler(ListPromptsRequestSchema, () => backend.listPrompts());
|
|
88
|
+
server.setRequestHandler(GetPromptRequestSchema, (req) => backend.getPrompt(req.params));
|
|
89
|
+
return server;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Starts the proxy MCP server on stdio.
|
|
93
|
+
*
|
|
94
|
+
* Convenience wrapper that calls {@link createProxyServer} then connects the
|
|
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.
|
|
100
|
+
*/
|
|
101
|
+
export async function startProxy(backend, options = {}) {
|
|
102
|
+
const server = createProxyServer(backend, options);
|
|
103
|
+
await server.connect(new StdioServerTransport());
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { Middleware } from './middleware.js';
|
|
2
|
+
export { compose } from './middleware.js';
|
|
3
|
+
export type { BackendConfig, BackendClient } from './backendClient.js';
|
|
4
|
+
export { createBackendClient } from './backendClient.js';
|
|
5
|
+
export type { ProxyOptions, ToolMiddleware, ResourceMiddleware, } from './core.js';
|
|
6
|
+
export { hasToolContent, createProxyServer, startProxy } from './core.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +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;AAOzD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Koa-style middleware composition for MCP request/response pipelines.
|
|
3
|
+
*
|
|
4
|
+
* A middleware is a pure function:
|
|
5
|
+
* (req, next) => Promise<Res>
|
|
6
|
+
*
|
|
7
|
+
* Calling `next(req)` passes control to the next middleware in the chain.
|
|
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])`):
|
|
12
|
+
* 1. auditMW enter → capture startTime
|
|
13
|
+
* 2. piiMW enter → call next
|
|
14
|
+
* 3. upstream call → raw response
|
|
15
|
+
* 4. piiMW exit → redact PII
|
|
16
|
+
* 5. auditMW exit → log clean result (never logs raw PII)
|
|
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`)
|
|
27
|
+
*/
|
|
28
|
+
export type Middleware<Req, Res> = (req: Req, next: (req: Req) => Promise<Res>) => Promise<Res>;
|
|
29
|
+
/**
|
|
30
|
+
* Composes an ordered array of middlewares into a single middleware using the
|
|
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.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const pipeline = compose([auditMW, piiMW]);
|
|
42
|
+
* const result = await pipeline(req, (r) => upstream.callTool(r.params));
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function compose<Req, Res>(middlewares: ReadonlyArray<Middleware<Req, Res>>): Middleware<Req, Res>;
|
|
46
|
+
/**
|
|
47
|
+
* Composes middlewares in response-processing order (internal helper used by mcpose core).
|
|
48
|
+
*
|
|
49
|
+
* The first element processes the response first — it is the innermost layer.
|
|
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.
|
|
60
|
+
*/
|
|
61
|
+
export declare function pipe<Req, Res>(middlewares: ReadonlyArray<Middleware<Req, Res>>): Middleware<Req, Res>;
|
|
62
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;;;GAOG;AACH,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;;;;;;;;;;;;;;;GAeG;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;;;;;;;;;;;;;;GAcG;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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Koa-style middleware composition for MCP request/response pipelines.
|
|
3
|
+
*
|
|
4
|
+
* A middleware is a pure function:
|
|
5
|
+
* (req, next) => Promise<Res>
|
|
6
|
+
*
|
|
7
|
+
* Calling `next(req)` passes control to the next middleware in the chain.
|
|
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])`):
|
|
12
|
+
* 1. auditMW enter → capture startTime
|
|
13
|
+
* 2. piiMW enter → call next
|
|
14
|
+
* 3. upstream call → raw response
|
|
15
|
+
* 4. piiMW exit → redact PII
|
|
16
|
+
* 5. auditMW exit → log clean result (never logs raw PII)
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Composes an ordered array of middlewares into a single middleware using the
|
|
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.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const pipeline = compose([auditMW, piiMW]);
|
|
33
|
+
* const result = await pipeline(req, (r) => upstream.callTool(r.params));
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function compose(middlewares) {
|
|
37
|
+
return (req, next) => {
|
|
38
|
+
let index = -1;
|
|
39
|
+
const dispatch = (i, currentReq) => {
|
|
40
|
+
if (i <= index) {
|
|
41
|
+
return Promise.reject(new Error('next() called multiple times'));
|
|
42
|
+
}
|
|
43
|
+
index = i;
|
|
44
|
+
const fn = i < middlewares.length ? middlewares[i] : (_r, n) => next(_r);
|
|
45
|
+
return Promise.resolve(fn(currentReq, (r) => dispatch(i + 1, r)));
|
|
46
|
+
};
|
|
47
|
+
return dispatch(0, req);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Composes middlewares in response-processing order (internal helper used by mcpose core).
|
|
52
|
+
*
|
|
53
|
+
* The first element processes the response first — it is the innermost layer.
|
|
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.
|
|
64
|
+
*/
|
|
65
|
+
export function pipe(middlewares) {
|
|
66
|
+
return compose([...middlewares].reverse());
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAeH;;;;;;;;;;;;;;;GAeG;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;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,IAAI,CAClB,WAAgD;IAEhD,OAAO,OAAO,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic test helpers for code that uses mcpose middleware.
|
|
3
|
+
*
|
|
4
|
+
* **No test framework imports** — this module has zero dependencies on
|
|
5
|
+
* vitest, jest, or mocha. Import it in any test environment.
|
|
6
|
+
*
|
|
7
|
+
* @module mcpose/testing
|
|
8
|
+
*/
|
|
9
|
+
import type { CallToolRequest, CallToolResult, ReadResourceResult, Tool, Resource, Prompt, CallToolRequestParams, GetPromptResult } from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
import type { ToolMiddleware } from './core.js';
|
|
11
|
+
import type { BackendClient } from './backendClient.js';
|
|
12
|
+
/**
|
|
13
|
+
* Calls a {@link ToolMiddleware} and narrows the result to {@link CallToolResult}.
|
|
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).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const result = await runToolMiddleware(mw, req, async () => mockResult);
|
|
26
|
+
* expect(result.content[0]).toMatchObject({ type: 'text' });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function runToolMiddleware(mw: ToolMiddleware, req: CallToolRequest, next: (req: CallToolRequest) => Promise<CallToolResult>): Promise<CallToolResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Configuration for the mock backend client.
|
|
32
|
+
*/
|
|
33
|
+
export interface MockBackendClientOptions {
|
|
34
|
+
/** Tools advertised by the mock upstream. Default: `[]`. */
|
|
35
|
+
tools?: Tool[];
|
|
36
|
+
/**
|
|
37
|
+
* Response for `callTool`. Can be a static result or a factory function
|
|
38
|
+
* that receives the call params and returns a per-call result.
|
|
39
|
+
*
|
|
40
|
+
* Default: `{ content: [{ type: 'text', text: 'mock response' }] }`
|
|
41
|
+
*/
|
|
42
|
+
callToolResponse?: CallToolResult | ((params: CallToolRequestParams) => CallToolResult);
|
|
43
|
+
/** Resources advertised by the mock upstream. Default: `[]`. */
|
|
44
|
+
resources?: Resource[];
|
|
45
|
+
/**
|
|
46
|
+
* Response for `readResource`.
|
|
47
|
+
*
|
|
48
|
+
* Default: `{ contents: [{ uri: '', text: 'mock resource' }] }`
|
|
49
|
+
*/
|
|
50
|
+
readResourceResponse?: ReadResourceResult;
|
|
51
|
+
/** Prompts advertised by the mock upstream. Default: `[]`. */
|
|
52
|
+
prompts?: Prompt[];
|
|
53
|
+
/**
|
|
54
|
+
* Response for `getPrompt`.
|
|
55
|
+
*
|
|
56
|
+
* Default: `{ messages: [] }`
|
|
57
|
+
*/
|
|
58
|
+
getPromptResponse?: GetPromptResult;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates a plain object implementing {@link BackendClient} for use in tests.
|
|
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.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const backend = createMockBackendClient({
|
|
70
|
+
* callToolResponse: { content: [{ type: 'text', text: 'John Doe: 123-45-6789' }] },
|
|
71
|
+
* });
|
|
72
|
+
* // Now compose real middlewares against it:
|
|
73
|
+
* const pipeline = compose([auditMW, piiToolMW]);
|
|
74
|
+
* const result = await pipeline(req, (r) => backend.callTool(r.params, undefined));
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function createMockBackendClient(options?: MockBackendClientOptions): BackendClient;
|
|
78
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;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;AAMxD;;;;;;;;;;;;;;;;GAgBG;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;AAMD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf;;;;;OAKG;IACH,gBAAgB,CAAC,EACb,cAAc,GACd,CAAC,CAAC,MAAM,EAAE,qBAAqB,KAAK,cAAc,CAAC,CAAC;IACxD,gEAAgE;IAChE,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,eAAe,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,aAAa,CAkCf"}
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { hasToolContent } from './core.js';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Pain Point 1: runToolMiddleware
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
/**
|
|
6
|
+
* Calls a {@link ToolMiddleware} and narrows the result to {@link CallToolResult}.
|
|
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).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const result = await runToolMiddleware(mw, req, async () => mockResult);
|
|
19
|
+
* expect(result.content[0]).toMatchObject({ type: 'text' });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export async function runToolMiddleware(mw, req, next) {
|
|
23
|
+
const result = await mw(req, next);
|
|
24
|
+
if (!hasToolContent(result)) {
|
|
25
|
+
throw new Error('runToolMiddleware: middleware returned legacy toolResult shape — expected { content: [...] }');
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Creates a plain object implementing {@link BackendClient} for use in tests.
|
|
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.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const backend = createMockBackendClient({
|
|
39
|
+
* callToolResponse: { content: [{ type: 'text', text: 'John Doe: 123-45-6789' }] },
|
|
40
|
+
* });
|
|
41
|
+
* // Now compose real middlewares against it:
|
|
42
|
+
* const pipeline = compose([auditMW, piiToolMW]);
|
|
43
|
+
* const result = await pipeline(req, (r) => backend.callTool(r.params, undefined));
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function createMockBackendClient(options = {}) {
|
|
47
|
+
return {
|
|
48
|
+
listTools: async () => ({
|
|
49
|
+
tools: options.tools ?? [],
|
|
50
|
+
}),
|
|
51
|
+
callTool: async (params) => {
|
|
52
|
+
const resp = options.callToolResponse;
|
|
53
|
+
if (typeof resp === 'function')
|
|
54
|
+
return resp(params);
|
|
55
|
+
return resp ?? { content: [{ type: 'text', text: 'mock response' }] };
|
|
56
|
+
},
|
|
57
|
+
listResources: async () => ({
|
|
58
|
+
resources: options.resources ?? [],
|
|
59
|
+
}),
|
|
60
|
+
readResource: async (_params) => options.readResourceResponse ?? {
|
|
61
|
+
contents: [{ uri: '', text: 'mock resource' }],
|
|
62
|
+
},
|
|
63
|
+
listPrompts: async () => ({
|
|
64
|
+
prompts: options.prompts ?? [],
|
|
65
|
+
}),
|
|
66
|
+
getPrompt: async (_params) => options.getPromptResponse ?? { messages: [] },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAI3C,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E;;;;;;;;;;;;;;;;GAgBG;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;AAuCD;;;;;;;;;;;;;;;;GAgBG;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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcpose",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Composable FP (Koa-style) middleware proxy builder for any MCP server",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./testing": {
|
|
14
|
+
"types": "./dist/testing.d.ts",
|
|
15
|
+
"default": "./dist/testing.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": ["dist", "README.md"],
|
|
19
|
+
"keywords": ["mcp", "middleware","functional-programming", "proxy", "compose", "model-context-protocol"],
|
|
20
|
+
"author": "Amir Gorji",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -p tsconfig.build.json",
|
|
24
|
+
"ts:ci": "tsc --noEmit",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"prepublishOnly": "npm run ts:ci && npm run test && npm run build"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": ">=1.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
33
|
+
"@types/node": "^22.0.0",
|
|
34
|
+
"typescript": "^5.0.0",
|
|
35
|
+
"vitest": "^3.0.0"
|
|
36
|
+
},"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/amir-gorji/mcpose.git"
|
|
39
|
+
}
|
|
40
|
+
}
|