cprime-supergateway 3.4.3

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.
Files changed (62) hide show
  1. package/.github/workflows/docker-publish.yaml +79 -0
  2. package/.husky/pre-commit +17 -0
  3. package/.prettierignore +8 -0
  4. package/.prettierrc +5 -0
  5. package/AGENTS.md +29 -0
  6. package/LICENSE +21 -0
  7. package/README.md +348 -0
  8. package/dist/gateways/sseToStdio.js +139 -0
  9. package/dist/gateways/stdioToSse.js +147 -0
  10. package/dist/gateways/stdioToStatefulStreamableHttp.js +188 -0
  11. package/dist/gateways/stdioToStatelessStreamableHttp.js +208 -0
  12. package/dist/gateways/stdioToWs.js +113 -0
  13. package/dist/gateways/streamableHttpToStdio.js +134 -0
  14. package/dist/index.js +266 -0
  15. package/dist/lib/corsOrigin.js +23 -0
  16. package/dist/lib/getLogger.js +44 -0
  17. package/dist/lib/getVersion.js +16 -0
  18. package/dist/lib/headers.js +31 -0
  19. package/dist/lib/onSignals.js +27 -0
  20. package/dist/lib/serializeCorsOrigin.js +6 -0
  21. package/dist/lib/sessionAccessCounter.js +77 -0
  22. package/dist/server/websocket.js +102 -0
  23. package/dist/services/EncryptionService.js +236 -0
  24. package/dist/types.js +1 -0
  25. package/docker/base.Dockerfile +9 -0
  26. package/docker/deno.Dockerfile +2 -0
  27. package/docker/uvx.Dockerfile +3 -0
  28. package/docker-bake.hcl +51 -0
  29. package/package.json +61 -0
  30. package/scripts/decrypt-sample.ts +34 -0
  31. package/scripts/encryption-play.ts +145 -0
  32. package/src/gateways/sseToStdio.ts +195 -0
  33. package/src/gateways/stdioToSse.ts +260 -0
  34. package/src/gateways/stdioToStatefulStreamableHttp.ts +274 -0
  35. package/src/gateways/stdioToStatelessStreamableHttp.ts +303 -0
  36. package/src/gateways/stdioToWs.ts +151 -0
  37. package/src/gateways/streamableHttpToStdio.ts +196 -0
  38. package/src/index.ts +286 -0
  39. package/src/lib/corsOrigin.ts +31 -0
  40. package/src/lib/getLogger.ts +83 -0
  41. package/src/lib/getVersion.ts +17 -0
  42. package/src/lib/headers.ts +55 -0
  43. package/src/lib/initMongoClient.ts +10 -0
  44. package/src/lib/mcpServerLogRepository.ts +48 -0
  45. package/src/lib/onSignals.ts +39 -0
  46. package/src/lib/serializeCorsOrigin.ts +14 -0
  47. package/src/lib/sessionAccessCounter.ts +118 -0
  48. package/src/server/websocket.ts +121 -0
  49. package/src/services/encryptionService.ts +309 -0
  50. package/src/types.ts +4 -0
  51. package/supergateway.png +0 -0
  52. package/tests/baseUrl.test.ts +62 -0
  53. package/tests/concurrency.test.ts +137 -0
  54. package/tests/helpers/mock-mcp-server.js +94 -0
  55. package/tests/protocolVersion.test.ts +60 -0
  56. package/tests/stdioToStatefulStreamableHttp.test.ts +70 -0
  57. package/tests/stdioToStatelessStreamableHttp.test.ts +71 -0
  58. package/tests/streamableHttpCli.test.ts +24 -0
  59. package/tests/streamableHttpToStdio.test.ts +64 -0
  60. package/tsconfig.build.json +8 -0
  61. package/tsconfig.json +12 -0
  62. package/tsconfig.test.json +10 -0
@@ -0,0 +1,79 @@
1
+ name: Publish Docker images
2
+
3
+ on:
4
+ workflow_dispatch: {}
5
+ push:
6
+ tags:
7
+ - "v*.*.*"
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ env:
13
+ GHCR_REGISTRY: ghcr.io/supercorp-ai
14
+ DOCKERHUB_REGISTRY: docker.io/supercorp
15
+
16
+ jobs:
17
+ publish:
18
+ name: Build and push supergateway container
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: read
22
+ id-token: write
23
+ packages: write
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@v4
27
+ with:
28
+ fetch-depth: 0
29
+
30
+ - name: Set up QEMU
31
+ uses: docker/setup-qemu-action@v3
32
+
33
+ - name: Set up Docker Buildx
34
+ uses: docker/setup-buildx-action@v3
35
+
36
+ - name: Login to GHCR
37
+ uses: docker/login-action@v3
38
+ with:
39
+ registry: ghcr.io
40
+ username: ${{ github.repository_owner }}
41
+ password: ${{ secrets.GITHUB_TOKEN }}
42
+
43
+ - name: Login to Docker Hub
44
+ uses: docker/login-action@v3
45
+ with:
46
+ registry: docker.io
47
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
48
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
49
+
50
+ - name: Extract version
51
+ id: ver
52
+ run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
53
+
54
+ - name: Docker meta
55
+ id: docker_meta
56
+ uses: docker/metadata-action@v5
57
+ with:
58
+ images: |
59
+ ${{ env.GHCR_REGISTRY }}/supergateway
60
+ ${{ env.DOCKERHUB_REGISTRY }}/supergateway
61
+ labels: |
62
+ org.opencontainers.image.title=Supergateway
63
+ org.opencontainers.image.version=${{ steps.ver.outputs.VERSION }}
64
+ org.opencontainers.image.source=${{ github.repository }}
65
+
66
+ - name: Build & push (Bake)
67
+ uses: docker/bake-action@v6
68
+ env:
69
+ VERSION: ${{ steps.ver.outputs.VERSION }}
70
+ with:
71
+ source: .
72
+ files: |
73
+ docker-bake.hcl
74
+ ${{ steps.docker_meta.outputs.bake-file }}
75
+ push: true
76
+ set: |
77
+ *.args.VERSION=${{ steps.ver.outputs.VERSION }}
78
+ *.cache-from=type=gha
79
+ *.cache-to=type=gha,mode=max
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ # Ensure we're in the project directory
5
+ GIT_ROOT=$(git rev-parse --show-toplevel)
6
+ cd "$GIT_ROOT" || exit 1
7
+
8
+ # Get staged files
9
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|jsx|ts|tsx|json|css|scss|md|yaml|yml)$' || true)
10
+
11
+ if [ -n "$STAGED_FILES" ]; then
12
+ echo "Formatting staged files before commit..."
13
+ # Use the installed Prettier to format only the staged files
14
+ ./node_modules/.bin/prettier --write --ignore-unknown $STAGED_FILES
15
+ # Re-add the formatted files to the staging area
16
+ git add $STAGED_FILES
17
+ fi
@@ -0,0 +1,8 @@
1
+ dist
2
+ node_modules
3
+ .git
4
+ .github
5
+ package-lock.json
6
+ .DS_Store
7
+ **/.hermit/**
8
+ **/cache/**
package/.prettierrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "semi": false,
3
+ "singleQuote": true,
4
+ "singleAttributePerLine": true
5
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,29 @@
1
+ ## Setup
2
+
3
+ Install Node.js v24 using nvm. After checking out the repository, run:
4
+
5
+ ```bash
6
+ nvm install 24
7
+ nvm use 24
8
+ npm install
9
+
10
+ # Build
11
+
12
+ Compile the TypeScript sources before running tests:
13
+
14
+ npm run build
15
+ ```
16
+
17
+ ## Running tests
18
+
19
+ Run the test suite with Node's test runner and ts-node to enable mocks:
20
+
21
+ ```bash
22
+ npm run test
23
+ ```
24
+
25
+ The `tests/helpers/mock-mcp-server.js` script provides a lightweight local MCP
26
+ server used during tests so everything runs offline. All tests should pass
27
+ without external downloads.
28
+
29
+ If network-dependent commands (like `npx -y @modelcontextprotocol/server-*`) fail, check network access.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Supercorp
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,348 @@
1
+ ![Supergateway: Run stdio MCP servers over SSE and WS](https://raw.githubusercontent.com/supercorp-ai/supergateway/main/supergateway.png)
2
+
3
+ **Supergateway** runs **MCP stdio-based servers** over **SSE (Server-Sent Events)** or **WebSockets (WS)** with one command. This is useful for remote access, debugging, or connecting to clients when your MCP server only supports stdio.
4
+
5
+ Supported by [Supermachine](https://supermachine.ai) (hosted MCPs), [Superinterface](https://superinterface.ai), and [Supercorp](https://supercorp.ai).
6
+
7
+ ## Installation & Usage
8
+
9
+ Run Supergateway via `npx`:
10
+
11
+ ```bash
12
+ npx -y supergateway --stdio "uvx mcp-server-git"
13
+ ```
14
+
15
+ - **`--stdio "command"`**: Command that runs an MCP server over stdio
16
+ - **`--sse "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"`**: SSE URL to connect to (SSE→stdio mode)
17
+ - **`--streamableHttp "https://mcp-server.example.com/mcp"`**: Streamable HTTP URL to connect to (StreamableHttp→stdio mode)
18
+ - **`--outputTransport stdio | sse | ws | streamableHttp`**: Output MCP transport (default: `sse` with `--stdio`, `stdio` with `--sse` or `--streamableHttp`)
19
+ - **`--port 8000`**: Port to listen on (stdio→SSE or stdio→WS mode, default: `8000`)
20
+ - **`--baseUrl "http://localhost:8000"`**: Base URL for SSE or WS clients (stdio→SSE mode; optional)
21
+ - **`--ssePath "/sse"`**: Path for SSE subscriptions (stdio→SSE mode, default: `/sse`)
22
+ - **`--messagePath "/message"`**: Path for messages (stdio→SSE or stdio→WS mode, default: `/message`)
23
+ - **`--streamableHttpPath "/mcp"`**: Path for Streamable HTTP (stdio→Streamable HTTP mode, default: `/mcp`)
24
+ - **`--stateful`**: Run stdio→Streamable HTTP in stateful mode
25
+ - **`--sessionTimeout 60000`**: Session timeout in milliseconds (stateful stdio→Streamable HTTP mode only)
26
+ - **`--header "x-user-id: 123"`**: Add one or more headers (stdio→SSE, SSE→stdio, or Streamable HTTP→stdio mode; can be used multiple times)
27
+ - **`--oauth2Bearer "some-access-token"`**: Adds an `Authorization` header with the provided Bearer token
28
+ - **`--logLevel debug | info | none`**: Controls logging level (default: `info`). Use `debug` for more verbose logs, `none` to suppress all logs.
29
+ - **`--cors`**: Enable CORS (stdio→SSE or stdio→WS mode). Use `--cors` with no values to allow all origins, or supply one or more allowed origins (e.g. `--cors "http://example.com"` or `--cors "/example\\.com$/"` for regex matching).
30
+ - **`--healthEndpoint /healthz`**: Register one or more endpoints (stdio→SSE or stdio→WS mode; can be used multiple times) that respond with `"ok"`
31
+
32
+ ## stdio → SSE
33
+
34
+ Expose an MCP stdio server as an SSE server:
35
+
36
+ ```bash
37
+ npx -y supergateway \
38
+ --stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
39
+ --port 8000 --baseUrl http://localhost:8000 \
40
+ --ssePath /sse --messagePath /message
41
+ ```
42
+
43
+ - **Subscribe to events**: `GET http://localhost:8000/sse`
44
+ - **Send messages**: `POST http://localhost:8000/message`
45
+
46
+ ## SSE → stdio
47
+
48
+ Connect to a remote SSE server and expose locally via stdio:
49
+
50
+ ```bash
51
+ npx -y supergateway --sse "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
52
+ ```
53
+
54
+ Useful for integrating remote SSE MCP servers into local command-line environments.
55
+
56
+ You can also pass headers when sending requests. This is useful for authentication:
57
+
58
+ ```bash
59
+ npx -y supergateway \
60
+ --sse "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app" \
61
+ --oauth2Bearer "some-access-token" \
62
+ --header "X-My-Header: another-header-value"
63
+ ```
64
+
65
+ ## Streamable HTTP → stdio
66
+
67
+ Connect to a remote Streamable HTTP server and expose locally via stdio:
68
+
69
+ ```bash
70
+ npx -y supergateway --streamableHttp "https://mcp-server.example.com/mcp"
71
+ ```
72
+
73
+ This mode is useful for connecting to MCP servers that use the newer Streamable HTTP transport protocol. Like SSE mode, you can also pass headers for authentication:
74
+
75
+ ```bash
76
+ npx -y supergateway \
77
+ --streamableHttp "https://mcp-server.example.com/mcp" \
78
+ --oauth2Bearer "some-access-token" \
79
+ --header "X-My-Header: another-header-value"
80
+ ```
81
+
82
+ ## stdio → Streamable HTTP
83
+
84
+ Expose an MCP stdio server as a Streamable HTTP server.
85
+
86
+ ### Stateless mode
87
+
88
+ ```bash
89
+ npx -y supergateway \
90
+ --stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
91
+ --outputTransport streamableHttp \
92
+ --port 8000
93
+ ```
94
+
95
+ ### Stateful mode
96
+
97
+ ```bash
98
+ npx -y supergateway \
99
+ --stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
100
+ --outputTransport streamableHttp --stateful \
101
+ --sessionTimeout 60000 --port 8000
102
+ ```
103
+
104
+ The Streamable HTTP endpoint defaults to `http://localhost:8000/mcp` (configurable via `--streamableHttpPath`).
105
+
106
+ ## stdio → WS
107
+
108
+ Expose an MCP stdio server as a WebSocket server:
109
+
110
+ ```bash
111
+ npx -y supergateway \
112
+ --stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
113
+ --port 8000 --outputTransport ws --messagePath /message
114
+ ```
115
+
116
+ - **WebSocket endpoint**: `ws://localhost:8000/message`
117
+
118
+ ## Example with MCP Inspector (stdio → SSE mode)
119
+
120
+ 1. **Run Supergateway**:
121
+
122
+ ```bash
123
+ npx -y supergateway --port 8000 \
124
+ --stdio "npx -y @modelcontextprotocol/server-filesystem /Users/MyName/Desktop"
125
+ ```
126
+
127
+ 2. **Use MCP Inspector**:
128
+
129
+ ```bash
130
+ npx @modelcontextprotocol/inspector
131
+ ```
132
+
133
+ You can now list tools, resources, or perform MCP actions via Supergateway.
134
+
135
+ ## Using with ngrok
136
+
137
+ Use [ngrok](https://ngrok.com/) to share your local MCP server publicly:
138
+
139
+ ```bash
140
+ npx -y supergateway --port 8000 --stdio "npx -y @modelcontextprotocol/server-filesystem ."
141
+
142
+ # In another terminal:
143
+ ngrok http 8000
144
+ ```
145
+
146
+ ngrok provides a public URL for remote access.
147
+
148
+ MCP server will be available at URL similar to: https://1234-567-890-12-456.ngrok-free.app/sse
149
+
150
+ ## Running with Docker
151
+
152
+ A Docker-based workflow avoids local Node.js setup. A ready-to-run Docker image is available here:
153
+ [supercorp/supergateway](https://hub.docker.com/r/supercorp/supergateway). Also on GHCR: [ghcr.io/supercorp-ai/supergateway](https://github.com/supercorp-ai/supergateway/pkgs/container/supergateway)
154
+
155
+ ### Using the Official Image
156
+
157
+ ```bash
158
+ docker run -it --rm -p 8000:8000 supercorp/supergateway \
159
+ --stdio "npx -y @modelcontextprotocol/server-filesystem /" \
160
+ --port 8000
161
+ ```
162
+
163
+ Docker pulls the image automatically. The MCP server runs in the container’s root directory (`/`). You can mount host directories if needed.
164
+
165
+ #### Images with dependencies
166
+
167
+ Pull any of these pre-built Supergateway images for various dependencies you might need.
168
+
169
+ - **uvx**
170
+ Supergateway + uv/uvx, so you can call `uvx` directly:
171
+
172
+ ```bash
173
+ docker run -it --rm -p 8000:8000 supercorp/supergateway:uvx \
174
+ --stdio "uvx mcp-server-fetch"
175
+ ```
176
+
177
+ - **deno**
178
+ Supergateway + Deno, ready to run Deno-based MCP servers:
179
+ ```bash
180
+ docker run -it --rm -p 8000:8000 supercorp/supergateway:deno \
181
+ --stdio "deno run -A jsr:@omedia/mcp-server-drupal --drupal-url https://your-drupal-server.com"
182
+ ```
183
+
184
+ ### Building the Image Yourself
185
+
186
+ Use provided Dockerfile:
187
+
188
+ ```bash
189
+ docker build -f docker/base.Dockerfile -t supergateway .
190
+
191
+ docker run -it --rm -p 8000:8000 supergateway --stdio "npx -y @modelcontextprotocol/server-filesystem ."
192
+ ```
193
+
194
+ ## Using with Claude Desktop (SSE → stdio mode)
195
+
196
+ Claude Desktop can use Supergateway’s SSE→stdio mode.
197
+
198
+ ### NPX-based MCP Server Example
199
+
200
+ ```json
201
+ {
202
+ "mcpServers": {
203
+ "supermachineExampleNpx": {
204
+ "command": "npx",
205
+ "args": [
206
+ "-y",
207
+ "supergateway",
208
+ "--sse",
209
+ "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
210
+ ]
211
+ }
212
+ }
213
+ }
214
+ ```
215
+
216
+ ### Docker-based MCP Server Example
217
+
218
+ ```json
219
+ {
220
+ "mcpServers": {
221
+ "supermachineExampleDocker": {
222
+ "command": "docker",
223
+ "args": [
224
+ "run",
225
+ "-i",
226
+ "--rm",
227
+ "supercorp/supergateway",
228
+ "--sse",
229
+ "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
230
+ ]
231
+ }
232
+ }
233
+ }
234
+ ```
235
+
236
+ ## Using with Cursor (SSE → stdio mode)
237
+
238
+ Cursor can also integrate with Supergateway in SSE→stdio mode. The configuration is similar to Claude Desktop.
239
+
240
+ ### NPX-based MCP Server Example for Cursor
241
+
242
+ ```json
243
+ {
244
+ "mcpServers": {
245
+ "cursorExampleNpx": {
246
+ "command": "npx",
247
+ "args": [
248
+ "-y",
249
+ "supergateway",
250
+ "--sse",
251
+ "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
252
+ ]
253
+ }
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### Docker-based MCP Server Example for Cursor
259
+
260
+ ```json
261
+ {
262
+ "mcpServers": {
263
+ "cursorExampleDocker": {
264
+ "command": "docker",
265
+ "args": [
266
+ "run",
267
+ "-i",
268
+ "--rm",
269
+ "supercorp/supergateway",
270
+ "--sse",
271
+ "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
272
+ ]
273
+ }
274
+ }
275
+ }
276
+ ```
277
+
278
+ **Note:** Although the setup supports sending headers via the `--header` flag, if you need to pass an Authorization header (which typically includes a space, e.g. `"Bearer 123"`), you must use the `--oauth2Bearer` flag due to a known Cursor bug with spaces in command-line arguments.
279
+
280
+ ## Why MCP?
281
+
282
+ [Model Context Protocol](https://spec.modelcontextprotocol.io/) standardizes AI tool interactions. Supergateway converts MCP stdio servers into SSE or WS services, simplifying integration and debugging with web-based or remote clients.
283
+
284
+ ## Advanced Configuration
285
+
286
+ Supergateway emphasizes modularity:
287
+
288
+ - Automatically manages JSON-RPC versioning.
289
+ - Retransmits package metadata where possible.
290
+ - stdio→SSE or stdio→WS mode logs via standard output; SSE→stdio mode logs via stderr.
291
+
292
+ ## Additional resources
293
+
294
+ - [Superargs](https://github.com/supercorp-ai/superargs) - provide arguments to MCP servers during runtime.
295
+
296
+ ## Contributors
297
+
298
+ - [@longfin](https://github.com/longfin)
299
+ - [@griffinqiu](https://github.com/griffinqiu)
300
+ - [@folkvir](https://github.com/folkvir)
301
+ - [@wizizm](https://github.com/wizizm)
302
+ - [@dtinth](https://github.com/dtinth)
303
+ - [@rajivml](https://github.com/rajivml)
304
+ - [@NicoBonaminio](https://github.com/NicoBonaminio)
305
+ - [@sibbl](https://github.com/sibbl)
306
+ - [@podarok](https://github.com/podarok)
307
+ - [@jmn8718](https://github.com/jmn8718)
308
+ - [@TraceIvan](https://github.com/TraceIvan)
309
+ - [@zhoufei0622](https://github.com/zhoufei0622)
310
+ - [@ezyang](https://github.com/ezyang)
311
+ - [@aleksadvaisly](https://github.com/aleksadvaisly)
312
+ - [@wuzhuoquan](https://github.com/wuzhuoquan)
313
+ - [@mantrakp04](https://github.com/mantrakp04)
314
+ - [@mheubi](https://github.com/mheubi)
315
+ - [@mjmendo](https://github.com/mjmendo)
316
+ - [@CyanMystery](https://github.com/CyanMystery)
317
+ - [@earonesty](https://github.com/earonesty)
318
+ - [@StefanBurscher](https://github.com/StefanBurscher)
319
+ - [@tarasyarema](https://github.com/tarasyarema)
320
+ - [@pcnfernando](https://github.com/pcnfernando)
321
+ - [@Areo-Joe](https://github.com/Areo-Joe)
322
+ - [@Joffref](https://github.com/Joffref)
323
+ - [@michaeljguarino](https://github.com/michaeljguarino)
324
+
325
+ ## Contributing
326
+
327
+ Issues and PRs welcome. Please open one if you encounter problems or have feature suggestions.
328
+
329
+ ## Tests
330
+
331
+ Supergateway is tested with the Node Test Runner.
332
+
333
+ To run the suite locally you need Node **24+**. Using [nvm](https://github.com/nvm-sh/nvm) you can install and activate it with:
334
+
335
+ ```bash
336
+ nvm install 24
337
+ nvm use 24
338
+ npm install
339
+ npm run build
340
+ npm test
341
+ ```
342
+
343
+ The `tests/helpers/mock-mcp-server.js` script provides a local MCP server so all
344
+ tests run without network access.
345
+
346
+ ## License
347
+
348
+ [MIT License](./LICENSE)
@@ -0,0 +1,139 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
3
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { z } from 'zod';
6
+ import { getVersion } from '../lib/getVersion.js';
7
+ import { onSignals } from '../lib/onSignals.js';
8
+ let sseClient;
9
+ const newInitializeSseClient = ({ message }) => {
10
+ const clientInfo = message.params?.clientInfo;
11
+ const clientCapabilities = message.params?.capabilities;
12
+ return new Client({
13
+ name: clientInfo?.name ?? 'supergateway',
14
+ version: clientInfo?.version ?? getVersion(),
15
+ }, {
16
+ capabilities: clientCapabilities ?? {},
17
+ });
18
+ };
19
+ const newFallbackSseClient = async ({ sseTransport, }) => {
20
+ const fallbackSseClient = new Client({
21
+ name: 'supergateway',
22
+ version: getVersion(),
23
+ }, {
24
+ capabilities: {},
25
+ });
26
+ await fallbackSseClient.connect(sseTransport);
27
+ return fallbackSseClient;
28
+ };
29
+ export async function sseToStdio(args) {
30
+ const { sseUrl, logger, headers } = args;
31
+ logger.info(` - sse: ${sseUrl}`);
32
+ logger.info(` - Headers: ${Object.keys(headers).length ? JSON.stringify(headers) : '(none)'}`);
33
+ logger.info('Connecting to SSE...');
34
+ onSignals({ logger });
35
+ const sseTransport = new SSEClientTransport(new URL(sseUrl), {
36
+ eventSourceInit: {
37
+ fetch: (...props) => {
38
+ const [url, init = {}] = props;
39
+ return fetch(url, { ...init, headers: { ...init.headers, ...headers } });
40
+ },
41
+ },
42
+ requestInit: {
43
+ headers,
44
+ },
45
+ });
46
+ sseTransport.onerror = (err) => {
47
+ logger.error('SSE error:', err);
48
+ };
49
+ sseTransport.onclose = () => {
50
+ logger.error('SSE connection closed');
51
+ process.exit(1);
52
+ };
53
+ const stdioServer = new Server({
54
+ name: 'supergateway',
55
+ version: getVersion(),
56
+ }, {
57
+ capabilities: {},
58
+ });
59
+ const stdioTransport = new StdioServerTransport();
60
+ await stdioServer.connect(stdioTransport);
61
+ const wrapResponse = (req, payload) => ({
62
+ jsonrpc: req.jsonrpc || '2.0',
63
+ id: req.id,
64
+ ...payload,
65
+ });
66
+ stdioServer.transport.onmessage = async (message) => {
67
+ const isRequest = 'method' in message && 'id' in message;
68
+ if (isRequest) {
69
+ logger.info('Stdio → SSE:', message);
70
+ const req = message;
71
+ let result;
72
+ try {
73
+ if (!sseClient) {
74
+ if (message.method === 'initialize') {
75
+ sseClient = newInitializeSseClient({
76
+ message,
77
+ });
78
+ const originalRequest = sseClient.request;
79
+ sseClient.request = async function (requestMessage, ...restArgs) {
80
+ // pass protocol version from original client
81
+ if (requestMessage.method === 'initialize' &&
82
+ message.params?.protocolVersion &&
83
+ requestMessage.params?.protocolVersion) {
84
+ requestMessage.params.protocolVersion =
85
+ message.params.protocolVersion;
86
+ }
87
+ result = await originalRequest.apply(this, [
88
+ requestMessage,
89
+ ...restArgs,
90
+ ]);
91
+ return result;
92
+ };
93
+ await sseClient.connect(sseTransport);
94
+ sseClient.request = originalRequest;
95
+ }
96
+ else {
97
+ logger.info('SSE client not initialized, creating fallback client');
98
+ sseClient = await newFallbackSseClient({ sseTransport });
99
+ }
100
+ logger.info('SSE connected');
101
+ }
102
+ else {
103
+ result = await sseClient.request(req, z.any());
104
+ }
105
+ }
106
+ catch (err) {
107
+ logger.error('Request error:', err);
108
+ const errorCode = err && typeof err === 'object' && 'code' in err
109
+ ? err.code
110
+ : -32000;
111
+ let errorMsg = err && typeof err === 'object' && 'message' in err
112
+ ? err.message
113
+ : 'Internal error';
114
+ const prefix = `MCP error ${errorCode}:`;
115
+ if (errorMsg.startsWith(prefix)) {
116
+ errorMsg = errorMsg.slice(prefix.length).trim();
117
+ }
118
+ const errorResp = wrapResponse(req, {
119
+ error: {
120
+ code: errorCode,
121
+ message: errorMsg,
122
+ },
123
+ });
124
+ process.stdout.write(JSON.stringify(errorResp) + '\n');
125
+ return;
126
+ }
127
+ const response = wrapResponse(req, result.hasOwnProperty('error')
128
+ ? { error: { ...result.error } }
129
+ : { result: { ...result } });
130
+ logger.info('Response:', response);
131
+ process.stdout.write(JSON.stringify(response) + '\n');
132
+ }
133
+ else {
134
+ logger.info('SSE → Stdio:', message);
135
+ process.stdout.write(JSON.stringify(message) + '\n');
136
+ }
137
+ };
138
+ logger.info('Stdio server listening');
139
+ }