@alsania-io/mcpnyx 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +251 -0
- package/dist/gateways/configToSse.d.ts +16 -0
- package/dist/gateways/configToSse.d.ts.map +1 -0
- package/dist/gateways/configToSse.js +144 -0
- package/dist/gateways/configToSse.js.map +1 -0
- package/dist/gateways/configToStreamableHttp.d.ts +16 -0
- package/dist/gateways/configToStreamableHttp.d.ts.map +1 -0
- package/dist/gateways/configToStreamableHttp.js +287 -0
- package/dist/gateways/configToStreamableHttp.js.map +1 -0
- package/dist/gateways/configToWs.d.ts +14 -0
- package/dist/gateways/configToWs.d.ts.map +1 -0
- package/dist/gateways/configToWs.js +145 -0
- package/dist/gateways/configToWs.js.map +1 -0
- package/dist/gateways/sseToSse.d.ts +16 -0
- package/dist/gateways/sseToSse.d.ts.map +1 -0
- package/dist/gateways/sseToSse.js +224 -0
- package/dist/gateways/sseToSse.js.map +1 -0
- package/dist/gateways/sseToStdio.d.ts +8 -0
- package/dist/gateways/sseToStdio.d.ts.map +1 -0
- package/dist/gateways/sseToStdio.js +130 -0
- package/dist/gateways/sseToStdio.js.map +1 -0
- package/dist/gateways/sseToWs.d.ts +14 -0
- package/dist/gateways/sseToWs.d.ts.map +1 -0
- package/dist/gateways/sseToWs.js +212 -0
- package/dist/gateways/sseToWs.js.map +1 -0
- package/dist/gateways/stdioToSse.d.ts +16 -0
- package/dist/gateways/stdioToSse.d.ts.map +1 -0
- package/dist/gateways/stdioToSse.js +134 -0
- package/dist/gateways/stdioToSse.js.map +1 -0
- package/dist/gateways/stdioToStatefulStreamableHttp.d.ts +15 -0
- package/dist/gateways/stdioToStatefulStreamableHttp.d.ts.map +1 -0
- package/dist/gateways/stdioToStatefulStreamableHttp.js +189 -0
- package/dist/gateways/stdioToStatefulStreamableHttp.js.map +1 -0
- package/dist/gateways/stdioToStatelessStreamableHttp.d.ts +14 -0
- package/dist/gateways/stdioToStatelessStreamableHttp.d.ts.map +1 -0
- package/dist/gateways/stdioToStatelessStreamableHttp.js +132 -0
- package/dist/gateways/stdioToStatelessStreamableHttp.js.map +1 -0
- package/dist/gateways/stdioToWs.d.ts +13 -0
- package/dist/gateways/stdioToWs.d.ts.map +1 -0
- package/dist/gateways/stdioToWs.js +115 -0
- package/dist/gateways/stdioToWs.js.map +1 -0
- package/dist/gateways/streamableHttpToSse.d.ts +16 -0
- package/dist/gateways/streamableHttpToSse.d.ts.map +1 -0
- package/dist/gateways/streamableHttpToSse.js +219 -0
- package/dist/gateways/streamableHttpToSse.js.map +1 -0
- package/dist/gateways/streamableHttpToStdio.d.ts +8 -0
- package/dist/gateways/streamableHttpToStdio.d.ts.map +1 -0
- package/dist/gateways/streamableHttpToStdio.js +124 -0
- package/dist/gateways/streamableHttpToStdio.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +424 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +78 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +59 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/corsOrigin.d.ts +6 -0
- package/dist/lib/corsOrigin.d.ts.map +1 -0
- package/dist/lib/corsOrigin.js +24 -0
- package/dist/lib/corsOrigin.js.map +1 -0
- package/dist/lib/getLogger.d.ts +6 -0
- package/dist/lib/getLogger.d.ts.map +1 -0
- package/dist/lib/getLogger.js +55 -0
- package/dist/lib/getLogger.js.map +1 -0
- package/dist/lib/getVersion.d.ts +2 -0
- package/dist/lib/getVersion.d.ts.map +1 -0
- package/dist/lib/getVersion.js +17 -0
- package/dist/lib/getVersion.js.map +1 -0
- package/dist/lib/headers.d.ts +9 -0
- package/dist/lib/headers.d.ts.map +1 -0
- package/dist/lib/headers.js +32 -0
- package/dist/lib/headers.js.map +1 -0
- package/dist/lib/mcpServerManager.d.ts +24 -0
- package/dist/lib/mcpServerManager.d.ts.map +1 -0
- package/dist/lib/mcpServerManager.js +347 -0
- package/dist/lib/mcpServerManager.js.map +1 -0
- package/dist/lib/onSignals.d.ts +14 -0
- package/dist/lib/onSignals.d.ts.map +1 -0
- package/dist/lib/onSignals.js +28 -0
- package/dist/lib/onSignals.js.map +1 -0
- package/dist/lib/serializeCorsOrigin.d.ts +5 -0
- package/dist/lib/serializeCorsOrigin.d.ts.map +1 -0
- package/dist/lib/serializeCorsOrigin.js +7 -0
- package/dist/lib/serializeCorsOrigin.js.map +1 -0
- package/dist/lib/sessionAccessCounter.d.ts +12 -0
- package/dist/lib/sessionAccessCounter.d.ts.map +1 -0
- package/dist/lib/sessionAccessCounter.js +78 -0
- package/dist/lib/sessionAccessCounter.js.map +1 -0
- package/dist/server/websocket.d.ts +22 -0
- package/dist/server/websocket.d.ts.map +1 -0
- package/dist/server/websocket.js +103 -0
- package/dist/server/websocket.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# MCPNyx
|
|
2
|
+
|
|
3
|
+
**MCPNyx** is an advanced MCP (Model Context Protocol) proxy server designed for the Nyx extension. It allows you to run multiple **MCP stdio-based** and **SSE-based** servers and expose them through a single unified SSE/HTTP/WebSocket endpoint. This enables Nyx and other MCP-compatible tools to connect to multiple remote MCP servers and tools via a single proxy.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔄 **Multiple Transport Protocols**: stdio, SSE, WebSocket, Streamable HTTP
|
|
8
|
+
- 🌐 **Unified Proxy**: Aggregate multiple MCP servers behind a single endpoint
|
|
9
|
+
- 🔌 **Flexible Routing**: Route requests to different MCP servers based on configuration
|
|
10
|
+
- 📡 **Real-time Communication**: SSE and WebSocket support for live updates
|
|
11
|
+
- ��️ **Stateful & Stateless Modes**: Choose between session-based or stateless operation
|
|
12
|
+
- 🎯 **Nyx Integration**: Optimized for use with the Nyx browser extension
|
|
13
|
+
|
|
14
|
+
## Installation & Usage
|
|
15
|
+
|
|
16
|
+
### Option 1: Run with npx (No Installation)
|
|
17
|
+
|
|
18
|
+
Run MCPNyx directly without installing:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx -y @alsania-io/mcpnyx@latest --config path/to/config.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Option 2: Global Installation
|
|
25
|
+
|
|
26
|
+
Install MCPNyx globally to use the `mcpnyx` or `nyxmcp` commands:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Install globally
|
|
30
|
+
npm install -g @alsania-io/mcpnyx
|
|
31
|
+
|
|
32
|
+
# Run with mcpnyx command
|
|
33
|
+
mcpnyx --config path/to/config.json
|
|
34
|
+
|
|
35
|
+
# Or use the nyxmcp alias
|
|
36
|
+
nyxmcp --config path/to/config.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### CLI Options
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
- `--config, -c <path>`: **(required)** Path to a JSON configuration file (see below)
|
|
43
|
+
- `--port <number>`: Port to run the proxy server on (default: `3055`)
|
|
44
|
+
- `--baseUrl <url>`: Base URL for SSE clients (default: `http://localhost:<port>`)
|
|
45
|
+
- `--ssePath <path>`: Path for SSE subscriptions (default: `/sse`)
|
|
46
|
+
- `--messagePath <path>`: Path for SSE messages (default: `/message`)
|
|
47
|
+
- `--logLevel <info|none>`: Set logging level (default: `info`)
|
|
48
|
+
- `--outputTransport stdio | sse | ws | streamableHttp`: Output MCP transport (default: `sse` with `--stdio` or `--config`, `stdio` with `--sse` or `--streamableHttp`)
|
|
49
|
+
- `--streamableHttpPath "/mcp"`: Path for Streamable HTTP (default: `/mcp`)
|
|
50
|
+
- `--stateful`: Run StreamableHttp in stateful mode
|
|
51
|
+
- `--sessionTimeout 60000`: Session timeout in milliseconds (stateful StreamableHttp modes only)
|
|
52
|
+
- `--header "x-user-id: 123"`: Add one or more custom headers
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
Create a JSON configuration file to define your MCP servers:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"filesystem": {
|
|
62
|
+
"command": "npx",
|
|
63
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/directory"]
|
|
64
|
+
},
|
|
65
|
+
"database": {
|
|
66
|
+
"command": "npx",
|
|
67
|
+
"args": ["-y", "@modelcontextprotocol/server-database", "postgresql://..."]
|
|
68
|
+
},
|
|
69
|
+
"remote-sse": {
|
|
70
|
+
"url": "https://remote-mcp-server.example.com/sse",
|
|
71
|
+
"transport": "sse"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Endpoints
|
|
78
|
+
|
|
79
|
+
Once running, MCPNyx exposes the following endpoints:
|
|
80
|
+
|
|
81
|
+
- **SSE endpoint**: `http://localhost:<port>/sse`
|
|
82
|
+
- **POST messages**: `http://localhost:<port>/message`
|
|
83
|
+
- **Streamable HTTP endpoint**: `http://localhost:<port>/mcp`
|
|
84
|
+
- **WebSocket endpoint**: `ws://localhost:<port>/message`
|
|
85
|
+
|
|
86
|
+
(You can customize the paths with `--ssePath`, `--messagePath`, and `--streamableHttpPath`.)
|
|
87
|
+
|
|
88
|
+
## Example Usage
|
|
89
|
+
|
|
90
|
+
### Basic Configuration
|
|
91
|
+
|
|
92
|
+
1. **Create a config file** (e.g., `nyx-config.json`):
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"mcpServers": {
|
|
97
|
+
"filesystem": {
|
|
98
|
+
"command": "npx",
|
|
99
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/documents"]
|
|
100
|
+
},
|
|
101
|
+
"alsaniamcp": {
|
|
102
|
+
"command": "npx",
|
|
103
|
+
"args": [
|
|
104
|
+
"-y",
|
|
105
|
+
"@alsania-io/mcp@latest",
|
|
106
|
+
"server",
|
|
107
|
+
"start",
|
|
108
|
+
"-c",
|
|
109
|
+
"/home/user/.mcp/config.json"
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
2. **Run MCPNyx**:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx -y @alsania-io/mcpnyx@latest --config nyx-config.json --port 3055
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Advanced Usage
|
|
123
|
+
|
|
124
|
+
#### stdio → SSE
|
|
125
|
+
|
|
126
|
+
Convert a stdio MCP server to SSE:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npx -y @alsania-io/mcpnyx --stdio "npx -y @modelcontextprotocol/server-filesystem /" \
|
|
130
|
+
--port 3055 --baseUrl http://localhost:3055
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### SSE → stdio
|
|
134
|
+
|
|
135
|
+
Convert an SSE MCP server to stdio:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx -y @alsania-io/mcpnyx --sse "https://remote-mcp-server.example.com/sse"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### Config → Streamable HTTP (Stateful)
|
|
142
|
+
|
|
143
|
+
Run multiple servers with stateful Streamable HTTP:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npx -y @alsania-io/mcpnyx --config ./config.json \
|
|
147
|
+
--outputTransport streamableHttp \
|
|
148
|
+
--port 3055 \
|
|
149
|
+
--stateful \
|
|
150
|
+
--sessionTimeout 300000
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Why MCP?
|
|
154
|
+
|
|
155
|
+
[Model Context Protocol](https://spec.modelcontextprotocol.io/) standardizes how AI tools exchange data. If your MCP server only speaks stdio, MCPNyx exposes an SSE/HTTP/WebSocket interface so remote clients (like the Nyx extension, MCP Inspector, or Claude Desktop) can connect without extra server changes. It also allows you to aggregate multiple MCP servers behind a single endpoint.
|
|
156
|
+
|
|
157
|
+
## Advanced Features
|
|
158
|
+
|
|
159
|
+
MCPNyx is designed with modularity and flexibility in mind:
|
|
160
|
+
|
|
161
|
+
- ✅ Supports both stdio and SSE MCP servers in one config
|
|
162
|
+
- ✅ Automatically derives the JSON-RPC version from incoming requests
|
|
163
|
+
- ✅ Package information (name and version) is retransmitted where possible
|
|
164
|
+
- ✅ Stdio-to-SSE mode uses standard logs; SSE-to-stdio mode logs via stderr
|
|
165
|
+
- ✅ SSE-to-SSE mode provides automatic reconnection with exponential backoff
|
|
166
|
+
- ✅ Health endpoints can be added for monitoring
|
|
167
|
+
- ✅ CORS support for cross-origin requests
|
|
168
|
+
- ✅ Custom headers for authentication and routing
|
|
169
|
+
|
|
170
|
+
## Integration with Nyx
|
|
171
|
+
|
|
172
|
+
MCPNyx is optimized for use with the **Nyx browser extension**, providing:
|
|
173
|
+
|
|
174
|
+
- 🎯 **Seamless MCP Integration**: Connect Nyx to multiple MCP servers
|
|
175
|
+
- 🔄 **Real-time Updates**: SSE support for live tool and resource updates
|
|
176
|
+
- 🛡️ **Secure Communication**: Support for authentication headers
|
|
177
|
+
- 📊 **Session Management**: Stateful mode for persistent connections
|
|
178
|
+
|
|
179
|
+
## Development
|
|
180
|
+
|
|
181
|
+
### Building from Source
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# Clone the repository
|
|
185
|
+
git clone https://github.com/alsania-io/mcpnyx.git
|
|
186
|
+
cd mcpnyx
|
|
187
|
+
|
|
188
|
+
# Install dependencies
|
|
189
|
+
npm install
|
|
190
|
+
|
|
191
|
+
# Build
|
|
192
|
+
npm run build
|
|
193
|
+
|
|
194
|
+
# Run locally
|
|
195
|
+
npm start -- --config ./config.json
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Development Mode
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npm run dev -- --config ./config.json
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Troubleshooting
|
|
205
|
+
|
|
206
|
+
### Port Already in Use
|
|
207
|
+
|
|
208
|
+
If port 3055 is already in use, specify a different port:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
npx -y @alsania-io/mcpnyx --config config.json --port 3007
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Connection Issues
|
|
215
|
+
|
|
216
|
+
- Ensure your MCP servers are properly configured in the config file
|
|
217
|
+
- Check that stdio commands are executable and have correct paths
|
|
218
|
+
- Verify SSE URLs are accessible and return proper SSE responses
|
|
219
|
+
- Check firewall settings if connecting to remote servers
|
|
220
|
+
|
|
221
|
+
### Logging
|
|
222
|
+
|
|
223
|
+
Enable detailed logging:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
npx -y @alsania-io/mcpnyx --config config.json --logLevel info
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Contributing
|
|
230
|
+
|
|
231
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT License - see LICENSE file for details
|
|
236
|
+
|
|
237
|
+
## Links
|
|
238
|
+
|
|
239
|
+
- **Homepage**: [alsania-io.com](https://alsania-io.com)
|
|
240
|
+
- **Repository**: [github.com/alsania-io/mcpnyx](https://github.com/alsania-io/mcpnyx)
|
|
241
|
+
- **Issues**: [github.com/alsania-io/mcpnyx/issues](https://github.com/alsania-io/mcpnyx/issues)
|
|
242
|
+
- **MCP Specification**: [modelcontextprotocol.io](https://modelcontextprotocol.io/)
|
|
243
|
+
|
|
244
|
+
## Acknowledgments
|
|
245
|
+
|
|
246
|
+
MCPNyx is part of the **Alsania I/O** ecosystem, built with the Alsania soverign vision and purpose in mind. It is designed for the Nyx extension.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
**Aligned with the Alsania AI Protocol v1.0**
|
|
251
|
+
*Imagined by Sigma. Powered by Echo.*
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CorsOptions } from 'cors';
|
|
2
|
+
import { Logger } from '../types.js';
|
|
3
|
+
export interface ConfigToSseArgs {
|
|
4
|
+
configPath: string;
|
|
5
|
+
port: number;
|
|
6
|
+
host: string;
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
ssePath: string;
|
|
9
|
+
messagePath: string;
|
|
10
|
+
logger: Logger;
|
|
11
|
+
corsOrigin: CorsOptions['origin'];
|
|
12
|
+
healthEndpoints: string[];
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export declare function configToSse(args: ConfigToSseArgs): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=configToSse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configToSse.d.ts","sourceRoot":"","sources":["../../src/gateways/configToSse.ts"],"names":[],"mappings":"AAEA,OAAa,EAAE,KAAK,WAAW,EAAE,MAAM,MAAM,CAAC;AAI9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAOrC,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAOD,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,iBAuJtD"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import bodyParser from 'body-parser';
|
|
3
|
+
import cors from 'cors';
|
|
4
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
5
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
6
|
+
import { getVersion } from '../lib/getVersion.js';
|
|
7
|
+
import { onSignals } from '../lib/onSignals.js';
|
|
8
|
+
import { serializeCorsOrigin } from '../lib/serializeCorsOrigin.js';
|
|
9
|
+
import { loadConfig } from '../lib/config.js';
|
|
10
|
+
import { McpServerManager } from '../lib/mcpServerManager.js';
|
|
11
|
+
const setResponseHeaders = ({ res, headers }) => Object.entries(headers).forEach(([key, value]) => {
|
|
12
|
+
res.setHeader(key, value);
|
|
13
|
+
});
|
|
14
|
+
export async function configToSse(args) {
|
|
15
|
+
const { configPath, port, host, baseUrl, ssePath, messagePath, logger, corsOrigin, healthEndpoints, headers } = args;
|
|
16
|
+
logger.info(` - config: ${configPath}`);
|
|
17
|
+
logger.info(` - Headers: ${Object.keys(headers).length ? JSON.stringify(headers) : '(none)'}`);
|
|
18
|
+
logger.info(` - host: ${host}`);
|
|
19
|
+
logger.info(` - port: ${port}`);
|
|
20
|
+
if (baseUrl) {
|
|
21
|
+
logger.info(` - baseUrl: ${baseUrl}`);
|
|
22
|
+
}
|
|
23
|
+
logger.info(` - ssePath: ${ssePath}`);
|
|
24
|
+
logger.info(` - messagePath: ${messagePath}`);
|
|
25
|
+
logger.info(` - CORS: ${corsOrigin ? `enabled (${serializeCorsOrigin({ corsOrigin })})` : 'disabled'}`);
|
|
26
|
+
logger.info(` - Health endpoints: ${healthEndpoints.length ? healthEndpoints.join(', ') : '(none)'}`);
|
|
27
|
+
const serverManager = new McpServerManager(logger);
|
|
28
|
+
const cleanup = async () => {
|
|
29
|
+
await serverManager.cleanup();
|
|
30
|
+
};
|
|
31
|
+
onSignals({ logger, cleanup });
|
|
32
|
+
let config;
|
|
33
|
+
try {
|
|
34
|
+
config = loadConfig(configPath);
|
|
35
|
+
logger.info(`Loaded config with ${Object.keys(config.mcpServers).length} servers`);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
logger.error(`Failed to load config: ${err}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
|
|
42
|
+
try {
|
|
43
|
+
await serverManager.addServer(serverName, serverConfig);
|
|
44
|
+
logger.info(`Successfully initialized server: ${serverName}`);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
logger.error(`Failed to initialize server ${serverName}: ${err}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const server = new Server({ name: 'mcp-nyx', version: getVersion() }, { capabilities: {} });
|
|
52
|
+
const sessions = {};
|
|
53
|
+
const app = express();
|
|
54
|
+
if (corsOrigin) {
|
|
55
|
+
app.use(cors({ origin: corsOrigin }));
|
|
56
|
+
}
|
|
57
|
+
app.use((req, res, next) => {
|
|
58
|
+
if (req.path === messagePath)
|
|
59
|
+
return next();
|
|
60
|
+
return bodyParser.json()(req, res, next);
|
|
61
|
+
});
|
|
62
|
+
for (const ep of healthEndpoints) {
|
|
63
|
+
app.get(ep, (_req, res) => {
|
|
64
|
+
setResponseHeaders({
|
|
65
|
+
res,
|
|
66
|
+
headers,
|
|
67
|
+
});
|
|
68
|
+
res.send('ok');
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
app.get(ssePath, async (req, res) => {
|
|
72
|
+
logger.info(`New SSE connection from ${req.ip}`);
|
|
73
|
+
setResponseHeaders({
|
|
74
|
+
res,
|
|
75
|
+
headers,
|
|
76
|
+
});
|
|
77
|
+
const sseTransport = new SSEServerTransport(`${baseUrl}${messagePath}`, res);
|
|
78
|
+
await server.connect(sseTransport);
|
|
79
|
+
const sessionId = sseTransport.sessionId;
|
|
80
|
+
if (sessionId) {
|
|
81
|
+
sessions[sessionId] = { transport: sseTransport, response: res };
|
|
82
|
+
}
|
|
83
|
+
sseTransport.onmessage = async (msg) => {
|
|
84
|
+
logger.info(`SSE → Servers (session ${sessionId}): ${JSON.stringify(msg)}`);
|
|
85
|
+
if ('method' in msg && 'id' in msg) {
|
|
86
|
+
try {
|
|
87
|
+
const response = await serverManager.handleRequest(msg);
|
|
88
|
+
logger.info(`Servers → SSE (session ${sessionId}):`);
|
|
89
|
+
logger.debug(`Servers → SSE (session ${sessionId}):`, response);
|
|
90
|
+
sseTransport.send(response);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
logger.error(`Error handling request in session ${sessionId}:`, err);
|
|
94
|
+
const errorResponse = {
|
|
95
|
+
jsonrpc: '2.0',
|
|
96
|
+
id: msg.id,
|
|
97
|
+
error: {
|
|
98
|
+
code: -32000,
|
|
99
|
+
message: 'Internal error',
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
sseTransport.send(errorResponse);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
sseTransport.onclose = () => {
|
|
107
|
+
logger.info(`SSE connection closed (session ${sessionId})`);
|
|
108
|
+
delete sessions[sessionId];
|
|
109
|
+
};
|
|
110
|
+
sseTransport.onerror = (err) => {
|
|
111
|
+
logger.error(`SSE error (session ${sessionId}):`, err);
|
|
112
|
+
delete sessions[sessionId];
|
|
113
|
+
};
|
|
114
|
+
req.on('close', () => {
|
|
115
|
+
logger.info(`Client disconnected (session ${sessionId})`);
|
|
116
|
+
delete sessions[sessionId];
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
app.post(messagePath, async (req, res) => {
|
|
120
|
+
const sessionId = req.query.sessionId;
|
|
121
|
+
setResponseHeaders({
|
|
122
|
+
res,
|
|
123
|
+
headers,
|
|
124
|
+
});
|
|
125
|
+
if (!sessionId) {
|
|
126
|
+
return res.status(400).send('Missing sessionId parameter');
|
|
127
|
+
}
|
|
128
|
+
const session = sessions[sessionId];
|
|
129
|
+
if (session?.transport?.handlePostMessage) {
|
|
130
|
+
logger.info(`POST to SSE transport (session ${sessionId})`);
|
|
131
|
+
await session.transport.handlePostMessage(req, res);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
res.status(503).send(`No active SSE connection for session ${sessionId}`);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
app.listen(port, host, () => {
|
|
138
|
+
logger.info(`Listening on ${host}:${port}`);
|
|
139
|
+
logger.info(`SSE endpoint: http://${host}:${port}${ssePath}`);
|
|
140
|
+
logger.info(`POST messages: http://${host}:${port}${messagePath}`);
|
|
141
|
+
});
|
|
142
|
+
logger.info('Config-to-SSE gateway ready');
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=configToSse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configToSse.js","sourceRoot":"","sources":["../../src/gateways/configToSse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CorsOptions } from 'cors';
|
|
2
|
+
import { Logger } from '../types.js';
|
|
3
|
+
export interface ConfigToStreamableHttpArgs {
|
|
4
|
+
configPath: string;
|
|
5
|
+
port: number;
|
|
6
|
+
host: string;
|
|
7
|
+
streamableHttpPath: string;
|
|
8
|
+
logger: Logger;
|
|
9
|
+
corsOrigin: CorsOptions['origin'];
|
|
10
|
+
healthEndpoints: string[];
|
|
11
|
+
headers: Record<string, string>;
|
|
12
|
+
stateless?: boolean;
|
|
13
|
+
sessionTimeout?: number | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function configToStreamableHttp(args: ConfigToStreamableHttpArgs): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=configToStreamableHttp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configToStreamableHttp.d.ts","sourceRoot":"","sources":["../../src/gateways/configToStreamableHttp.ts"],"names":[],"mappings":"AACA,OAAa,EAAE,KAAK,WAAW,EAAE,MAAM,MAAM,CAAC;AAI9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC,MAAM,WAAW,0BAA0B;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAOD,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,0BAA0B,iBAkU5E"}
|