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.
- package/.github/workflows/docker-publish.yaml +79 -0
- package/.husky/pre-commit +17 -0
- package/.prettierignore +8 -0
- package/.prettierrc +5 -0
- package/AGENTS.md +29 -0
- package/LICENSE +21 -0
- package/README.md +348 -0
- package/dist/gateways/sseToStdio.js +139 -0
- package/dist/gateways/stdioToSse.js +147 -0
- package/dist/gateways/stdioToStatefulStreamableHttp.js +188 -0
- package/dist/gateways/stdioToStatelessStreamableHttp.js +208 -0
- package/dist/gateways/stdioToWs.js +113 -0
- package/dist/gateways/streamableHttpToStdio.js +134 -0
- package/dist/index.js +266 -0
- package/dist/lib/corsOrigin.js +23 -0
- package/dist/lib/getLogger.js +44 -0
- package/dist/lib/getVersion.js +16 -0
- package/dist/lib/headers.js +31 -0
- package/dist/lib/onSignals.js +27 -0
- package/dist/lib/serializeCorsOrigin.js +6 -0
- package/dist/lib/sessionAccessCounter.js +77 -0
- package/dist/server/websocket.js +102 -0
- package/dist/services/EncryptionService.js +236 -0
- package/dist/types.js +1 -0
- package/docker/base.Dockerfile +9 -0
- package/docker/deno.Dockerfile +2 -0
- package/docker/uvx.Dockerfile +3 -0
- package/docker-bake.hcl +51 -0
- package/package.json +61 -0
- package/scripts/decrypt-sample.ts +34 -0
- package/scripts/encryption-play.ts +145 -0
- package/src/gateways/sseToStdio.ts +195 -0
- package/src/gateways/stdioToSse.ts +260 -0
- package/src/gateways/stdioToStatefulStreamableHttp.ts +274 -0
- package/src/gateways/stdioToStatelessStreamableHttp.ts +303 -0
- package/src/gateways/stdioToWs.ts +151 -0
- package/src/gateways/streamableHttpToStdio.ts +196 -0
- package/src/index.ts +286 -0
- package/src/lib/corsOrigin.ts +31 -0
- package/src/lib/getLogger.ts +83 -0
- package/src/lib/getVersion.ts +17 -0
- package/src/lib/headers.ts +55 -0
- package/src/lib/initMongoClient.ts +10 -0
- package/src/lib/mcpServerLogRepository.ts +48 -0
- package/src/lib/onSignals.ts +39 -0
- package/src/lib/serializeCorsOrigin.ts +14 -0
- package/src/lib/sessionAccessCounter.ts +118 -0
- package/src/server/websocket.ts +121 -0
- package/src/services/encryptionService.ts +309 -0
- package/src/types.ts +4 -0
- package/supergateway.png +0 -0
- package/tests/baseUrl.test.ts +62 -0
- package/tests/concurrency.test.ts +137 -0
- package/tests/helpers/mock-mcp-server.js +94 -0
- package/tests/protocolVersion.test.ts +60 -0
- package/tests/stdioToStatefulStreamableHttp.test.ts +70 -0
- package/tests/stdioToStatelessStreamableHttp.test.ts +71 -0
- package/tests/streamableHttpCli.test.ts +24 -0
- package/tests/streamableHttpToStdio.test.ts +64 -0
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +12 -0
- 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
|
package/.prettierignore
ADDED
package/.prettierrc
ADDED
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
|
+

|
|
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
|
+
}
|