@@ -3,6 +3,9 @@ import express from 'express';
33import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' ;
44import { logger } from '../logging/logging.js' ;
55import { createServer } from './server.js' ;
6+ import { randomUUID } from 'node:crypto' ;
7+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js' ;
8+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js' ;
69
710export const connectStdioTransport = ( ) => {
811 const server = createServer ( {
@@ -45,3 +48,80 @@ export const connectSSETransport = (port: number) => {
4548 logger . info ( `Connecting to SSE transport on port: ${ port } ` ) ;
4649 app . listen ( port ) ;
4750} ;
51+
52+ export const connectHttpTransport = ( port : number ) => {
53+ const app = express ( ) ;
54+ app . use ( express . json ( ) ) ;
55+
56+ const httpTransports : { [ sessionId : string ] : StreamableHTTPServerTransport } = { } ;
57+
58+ app . post ( '/mcp' , async ( req , res ) => {
59+ const sessionIdFromHeader = req . headers [ 'mcp-session-id' ] as string | undefined ;
60+ let transport : StreamableHTTPServerTransport ;
61+
62+ if ( sessionIdFromHeader && httpTransports [ sessionIdFromHeader ] ) {
63+ transport = httpTransports [ sessionIdFromHeader ] ;
64+ } else if ( ! sessionIdFromHeader && isInitializeRequest ( req . body ) ) {
65+ const argocdBaseUrl = ( req . headers [ 'x-argocd-base-url' ] as string ) || '' ;
66+ const argocdApiToken = ( req . headers [ 'x-argocd-api-token' ] as string ) || '' ;
67+
68+ if ( argocdBaseUrl == '' || argocdApiToken == '' ) {
69+ res
70+ . status ( 400 )
71+ . send ( 'x-argocd-base-url and x-argocd-api-token must be provided in headers.' ) ;
72+ return ;
73+ }
74+
75+ transport = new StreamableHTTPServerTransport ( {
76+ sessionIdGenerator : ( ) => randomUUID ( ) ,
77+ onsessioninitialized : ( newSessionId ) => {
78+ httpTransports [ newSessionId ] = transport ;
79+ }
80+ } ) ;
81+
82+ transport . onclose = ( ) => {
83+ if ( transport . sessionId ) {
84+ delete httpTransports [ transport . sessionId ] ;
85+ }
86+ } ;
87+
88+ const server = createServer ( {
89+ argocdBaseUrl,
90+ argocdApiToken
91+ } ) ;
92+
93+ await server . connect ( transport ) ;
94+ } else {
95+ const errorMsg = sessionIdFromHeader
96+ ? `Invalid or expired session ID: ${ sessionIdFromHeader } `
97+ : 'Bad Request: Not an initialization request and no valid session ID provided.' ;
98+ res . status ( 400 ) . json ( {
99+ jsonrpc : '2.0' ,
100+ error : {
101+ code : - 32000 ,
102+ message : errorMsg
103+ } ,
104+ id : req . body ?. id !== undefined ? req . body . id : null
105+ } ) ;
106+ return ;
107+ }
108+
109+ await transport . handleRequest ( req , res , req . body ) ;
110+ } ) ;
111+
112+ const handleSessionRequest = async ( req : express . Request , res : express . Response ) => {
113+ const sessionId = req . headers [ 'mcp-session-id' ] as string | undefined ;
114+ if ( ! sessionId || ! httpTransports [ sessionId ] ) {
115+ res . status ( 400 ) . send ( 'Invalid or missing session ID' ) ;
116+ return ;
117+ }
118+ const transport = httpTransports [ sessionId ] ;
119+ await transport . handleRequest ( req , res ) ;
120+ } ;
121+
122+ app . get ( '/mcp' , handleSessionRequest ) ;
123+ app . delete ( '/mcp' , handleSessionRequest ) ;
124+
125+ logger . info ( `Connecting to Http Stream transport on port: ${ port } ` ) ;
126+ app . listen ( port ) ;
127+ } ;
0 commit comments