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,70 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { spawn, ChildProcess } from 'child_process'
|
|
4
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
5
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
|
6
|
+
|
|
7
|
+
const PORT = 11004
|
|
8
|
+
const MCP_URL = `http://localhost:${PORT}/mcp`
|
|
9
|
+
|
|
10
|
+
let gatewayProc: ChildProcess
|
|
11
|
+
|
|
12
|
+
test.before(() => {
|
|
13
|
+
gatewayProc = spawn(
|
|
14
|
+
'npm',
|
|
15
|
+
[
|
|
16
|
+
'run',
|
|
17
|
+
'start',
|
|
18
|
+
'--',
|
|
19
|
+
'--stdio',
|
|
20
|
+
'node tests/helpers/mock-mcp-server.js stdio',
|
|
21
|
+
'--outputTransport',
|
|
22
|
+
'streamableHttp',
|
|
23
|
+
'--stateful',
|
|
24
|
+
'--port',
|
|
25
|
+
String(PORT),
|
|
26
|
+
'--streamableHttpPath',
|
|
27
|
+
'/mcp',
|
|
28
|
+
],
|
|
29
|
+
{ stdio: 'ignore', shell: false },
|
|
30
|
+
)
|
|
31
|
+
gatewayProc.unref()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test.after(async () => {
|
|
35
|
+
gatewayProc.kill('SIGINT')
|
|
36
|
+
await new Promise((resolve) => gatewayProc.once('exit', resolve))
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('stdioToStatefulStreamableHttp listTools and callTool', async () => {
|
|
40
|
+
const transport = new StreamableHTTPClientTransport(new URL(MCP_URL))
|
|
41
|
+
const client = new Client({ name: 'stateful-test', version: '1.0.0' })
|
|
42
|
+
await new Promise((r) => setTimeout(r, 2000))
|
|
43
|
+
await client.connect(transport)
|
|
44
|
+
|
|
45
|
+
assert.ok(transport.sessionId, 'sessionId should be set after connect')
|
|
46
|
+
|
|
47
|
+
const { tools } = await client.listTools()
|
|
48
|
+
assert.ok(tools.some((t) => t.name === 'add'))
|
|
49
|
+
|
|
50
|
+
type Reply = { content: Array<{ text: string }> }
|
|
51
|
+
const reply1 = (await client.callTool({
|
|
52
|
+
name: 'add',
|
|
53
|
+
arguments: { a: 1, b: 2 },
|
|
54
|
+
})) as Reply
|
|
55
|
+
|
|
56
|
+
assert.strictEqual(reply1.content[0].text, 'The sum of 1 and 2 is 3.')
|
|
57
|
+
|
|
58
|
+
const reply2 = (await client.callTool({
|
|
59
|
+
name: 'add',
|
|
60
|
+
arguments: { a: 3, b: 4 },
|
|
61
|
+
})) as Reply
|
|
62
|
+
|
|
63
|
+
assert.strictEqual(reply2.content[0].text, 'The sum of 3 and 4 is 7.')
|
|
64
|
+
|
|
65
|
+
await transport.terminateSession()
|
|
66
|
+
assert.strictEqual(transport.sessionId, undefined)
|
|
67
|
+
|
|
68
|
+
await client.close()
|
|
69
|
+
transport.close()
|
|
70
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { spawn, ChildProcess } from 'child_process'
|
|
4
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
5
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
|
6
|
+
|
|
7
|
+
const PORT = 11005
|
|
8
|
+
const MCP_URL = `http://localhost:${PORT}/mcp`
|
|
9
|
+
|
|
10
|
+
let gatewayProc: ChildProcess
|
|
11
|
+
|
|
12
|
+
test.before(() => {
|
|
13
|
+
gatewayProc = spawn(
|
|
14
|
+
'npm',
|
|
15
|
+
[
|
|
16
|
+
'run',
|
|
17
|
+
'start',
|
|
18
|
+
'--',
|
|
19
|
+
'--stdio',
|
|
20
|
+
'node tests/helpers/mock-mcp-server.js stdio',
|
|
21
|
+
'--outputTransport',
|
|
22
|
+
'streamableHttp',
|
|
23
|
+
'--port',
|
|
24
|
+
String(PORT),
|
|
25
|
+
'--streamableHttpPath',
|
|
26
|
+
'/mcp',
|
|
27
|
+
],
|
|
28
|
+
{ stdio: 'ignore', shell: false },
|
|
29
|
+
)
|
|
30
|
+
gatewayProc.unref()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test.after(async () => {
|
|
34
|
+
gatewayProc.kill('SIGINT')
|
|
35
|
+
await new Promise((resolve) => gatewayProc.once('exit', resolve))
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('stdioToStatelessStreamableHttp listTools and callTool', async () => {
|
|
39
|
+
const transport = new StreamableHTTPClientTransport(new URL(MCP_URL))
|
|
40
|
+
const client = new Client({ name: 'stateless-test', version: '1.0.0' })
|
|
41
|
+
await new Promise((r) => setTimeout(r, 2000))
|
|
42
|
+
await client.connect(transport)
|
|
43
|
+
|
|
44
|
+
assert.strictEqual(transport.sessionId, undefined)
|
|
45
|
+
|
|
46
|
+
const { tools } = await client.listTools()
|
|
47
|
+
assert.ok(tools.some((t) => t.name === 'add'))
|
|
48
|
+
|
|
49
|
+
type Reply = { content: Array<{ text: string }> }
|
|
50
|
+
const reply1 = (await client.callTool({
|
|
51
|
+
name: 'add',
|
|
52
|
+
arguments: { a: 4, b: 5 },
|
|
53
|
+
})) as Reply
|
|
54
|
+
|
|
55
|
+
assert.strictEqual(reply1.content[0].text, 'The sum of 4 and 5 is 9.')
|
|
56
|
+
|
|
57
|
+
const reply2 = (await client.callTool({
|
|
58
|
+
name: 'add',
|
|
59
|
+
arguments: { a: 2, b: 7 },
|
|
60
|
+
})) as Reply
|
|
61
|
+
|
|
62
|
+
assert.strictEqual(reply2.content[0].text, 'The sum of 2 and 7 is 9.')
|
|
63
|
+
|
|
64
|
+
await client.close()
|
|
65
|
+
transport.close()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('GET returns 405', async () => {
|
|
69
|
+
const res = await fetch(MCP_URL)
|
|
70
|
+
assert.strictEqual(res.status, 405)
|
|
71
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import yargs from 'yargs'
|
|
4
|
+
import { hideBin } from 'yargs/helpers'
|
|
5
|
+
|
|
6
|
+
test('yargs parses streamableHttp outputTransport', () => {
|
|
7
|
+
const argv = yargs(
|
|
8
|
+
hideBin([
|
|
9
|
+
'node',
|
|
10
|
+
'',
|
|
11
|
+
'--stdio',
|
|
12
|
+
'true',
|
|
13
|
+
'--outputTransport',
|
|
14
|
+
'streamableHttp',
|
|
15
|
+
]),
|
|
16
|
+
)
|
|
17
|
+
.option('stdio', { type: 'string' })
|
|
18
|
+
.option('outputTransport', {
|
|
19
|
+
type: 'string',
|
|
20
|
+
choices: ['stdio', 'sse', 'ws', 'streamableHttp'],
|
|
21
|
+
})
|
|
22
|
+
.parseSync()
|
|
23
|
+
assert.strictEqual(argv.outputTransport, 'streamableHttp')
|
|
24
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { spawn, ChildProcess } from 'child_process'
|
|
4
|
+
|
|
5
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
6
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|
7
|
+
|
|
8
|
+
const MCP_PORT = 11002
|
|
9
|
+
const MCP_URL = `http://localhost:${MCP_PORT}/mcp`
|
|
10
|
+
|
|
11
|
+
let serverProc: ChildProcess | undefined
|
|
12
|
+
|
|
13
|
+
function spawnMcpServer(): Promise<ChildProcess> {
|
|
14
|
+
return new Promise((res, rej) => {
|
|
15
|
+
const proc = spawn(
|
|
16
|
+
'node',
|
|
17
|
+
['tests/helpers/mock-mcp-server.js', 'streamableHttp'],
|
|
18
|
+
{
|
|
19
|
+
env: { ...process.env, PORT: String(MCP_PORT) },
|
|
20
|
+
shell: false,
|
|
21
|
+
stdio: ['inherit', 'pipe', 'inherit'],
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
proc.stdout.setEncoding('utf8')
|
|
26
|
+
proc.stdout.on('data', (chunk: string) => {
|
|
27
|
+
if (chunk.includes('MCP Streamable HTTP Server listening')) {
|
|
28
|
+
res(proc)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
proc.on('error', rej)
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test.before(async () => {
|
|
37
|
+
serverProc = await spawnMcpServer()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test.after(() => serverProc?.kill('SIGINT'))
|
|
41
|
+
|
|
42
|
+
test('streamableHttpToStdio listTools and callTool', async () => {
|
|
43
|
+
const gatewayCmd = ['npm', 'run', 'start', '--', '--streamableHttp', MCP_URL]
|
|
44
|
+
|
|
45
|
+
const transport = new StdioClientTransport({
|
|
46
|
+
command: gatewayCmd[0],
|
|
47
|
+
args: gatewayCmd.slice(1),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const client = new Client({ name: 'gateway-test', version: '1.0.0' })
|
|
51
|
+
await client.connect(transport)
|
|
52
|
+
|
|
53
|
+
const { tools } = await client.listTools()
|
|
54
|
+
assert.ok(tools.some((t) => t.name === 'add'))
|
|
55
|
+
|
|
56
|
+
type Reply = { content: Array<{ text: string }> }
|
|
57
|
+
const reply = (await client.callTool({
|
|
58
|
+
name: 'add',
|
|
59
|
+
arguments: { a: 2, b: 3 },
|
|
60
|
+
})) as Reply
|
|
61
|
+
|
|
62
|
+
assert.strictEqual(reply.content[0].text, 'The sum of 2 and 3 is 5.')
|
|
63
|
+
await client.close()
|
|
64
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "NodeNext",
|
|
4
|
+
"moduleResolution": "NodeNext",
|
|
5
|
+
"target": "ES2022",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"forceConsistentCasingInFileNames": true,
|
|
9
|
+
"outDir": "dist"
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/**/*", "tests/**/*"]
|
|
12
|
+
}
|