@@ -36,13 +36,6 @@ type sseSession struct {
36
36
// content. This can be used to inject context values from headers, for example.
37
37
type SSEContextFunc func (ctx context.Context , r * http.Request ) context.Context
38
38
39
- // DynamicBasePathFunc allows the user to provide a function to generate the
40
- // base path for a given request and sessionID. This is useful for cases where
41
- // the base path is not known at the time of SSE server creation, such as when
42
- // using a reverse proxy or when the base path is dynamically generated. The
43
- // function should return the base path (e.g., "/mcp/tenant123").
44
- type DynamicBasePathFunc func (r * http.Request , sessionID string ) string
45
-
46
39
func (s * sseSession ) SessionID () string {
47
40
return s .sessionID
48
41
}
@@ -100,7 +93,7 @@ type SSEServer struct {
100
93
sseEndpoint string
101
94
sessions sync.Map
102
95
srv * http.Server
103
- contextFunc SSEContextFunc
96
+ contextFunc HTTPContextFunc
104
97
dynamicBasePathFunc DynamicBasePathFunc
105
98
106
99
keepAlive bool
@@ -109,37 +102,41 @@ type SSEServer struct {
109
102
mu sync.RWMutex
110
103
}
111
104
112
- // SSEOption defines a function type for configuring SSEServer
113
- type SSEOption func (* SSEServer )
105
+ // Ensure SSEServer implements httpTransportConfigurable
106
+ var _ httpTransportConfigurable = (* SSEServer )( nil )
114
107
115
- // WithBaseURL sets the base URL for the SSE server
116
- func WithBaseURL (baseURL string ) SSEOption {
117
- return func (s * SSEServer ) {
118
- if baseURL != "" {
119
- u , err := url .Parse (baseURL )
120
- if err != nil {
121
- return
122
- }
123
- if u .Scheme != "http" && u .Scheme != "https" {
124
- return
125
- }
126
- // Check if the host is empty or only contains a port
127
- if u .Host == "" || strings .HasPrefix (u .Host , ":" ) {
128
- return
129
- }
130
- if len (u .Query ()) > 0 {
131
- return
132
- }
108
+ func (s * SSEServer ) setBasePath (basePath string ) {
109
+ s .basePath = normalizeURLPath (basePath )
110
+ }
111
+
112
+ func (s * SSEServer ) setDynamicBasePath (fn DynamicBasePathFunc ) {
113
+ if fn != nil {
114
+ s .dynamicBasePathFunc = func (r * http.Request , sid string ) string {
115
+ bp := fn (r , sid )
116
+ return normalizeURLPath (bp )
133
117
}
134
- s .baseURL = strings .TrimSuffix (baseURL , "/" )
135
118
}
136
119
}
137
120
138
- // WithStaticBasePath adds a new option for setting a static base path
139
- func WithStaticBasePath (basePath string ) SSEOption {
140
- return func (s * SSEServer ) {
141
- s .basePath = normalizeURLPath (basePath )
142
- }
121
+ func (s * SSEServer ) setKeepAliveInterval (interval time.Duration ) {
122
+ s .keepAlive = true
123
+ s .keepAliveInterval = interval
124
+ }
125
+
126
+ func (s * SSEServer ) setKeepAlive (keepAlive bool ) {
127
+ s .keepAlive = keepAlive
128
+ }
129
+
130
+ func (s * SSEServer ) setContextFunc (fn HTTPContextFunc ) {
131
+ s .contextFunc = fn
132
+ }
133
+
134
+ func (s * SSEServer ) setHTTPServer (srv * http.Server ) {
135
+ s .srv = srv
136
+ }
137
+
138
+ func (s * SSEServer ) setBaseURL (baseURL string ) {
139
+ s .baseURL = baseURL
143
140
}
144
141
145
142
// WithBasePath adds a new option for setting a static base path.
@@ -151,26 +148,11 @@ func WithBasePath(basePath string) SSEOption {
151
148
return WithStaticBasePath (basePath )
152
149
}
153
150
154
- // WithDynamicBasePath accepts a function for generating the base path. This is
155
- // useful for cases where the base path is not known at the time of SSE server
156
- // creation, such as when using a reverse proxy or when the server is mounted
157
- // at a dynamic path.
158
- func WithDynamicBasePath (fn DynamicBasePathFunc ) SSEOption {
159
- return func (s * SSEServer ) {
160
- if fn != nil {
161
- s .dynamicBasePathFunc = func (r * http.Request , sid string ) string {
162
- bp := fn (r , sid )
163
- return normalizeURLPath (bp )
164
- }
165
- }
166
- }
167
- }
168
-
169
151
// WithMessageEndpoint sets the message endpoint path
170
152
func WithMessageEndpoint (endpoint string ) SSEOption {
171
- return func (s * SSEServer ) {
153
+ return sseOption ( func (s * SSEServer ) {
172
154
s .messageEndpoint = endpoint
173
- }
155
+ })
174
156
}
175
157
176
158
// WithAppendQueryToMessageEndpoint configures the SSE server to append the original request's
@@ -179,53 +161,37 @@ func WithMessageEndpoint(endpoint string) SSEOption {
179
161
// SSE connection request and carry them over to subsequent message requests, maintaining
180
162
// context or authentication details across the communication channel.
181
163
func WithAppendQueryToMessageEndpoint () SSEOption {
182
- return func (s * SSEServer ) {
164
+ return sseOption ( func (s * SSEServer ) {
183
165
s .appendQueryToMessageEndpoint = true
184
- }
166
+ })
185
167
}
186
168
187
169
// WithUseFullURLForMessageEndpoint controls whether the SSE server returns a complete URL (including baseURL)
188
170
// or just the path portion for the message endpoint. Set to false when clients will concatenate
189
171
// the baseURL themselves to avoid malformed URLs like "http://localhost/mcphttp://localhost/mcp/message".
190
172
func WithUseFullURLForMessageEndpoint (useFullURLForMessageEndpoint bool ) SSEOption {
191
- return func (s * SSEServer ) {
173
+ return sseOption ( func (s * SSEServer ) {
192
174
s .useFullURLForMessageEndpoint = useFullURLForMessageEndpoint
193
- }
175
+ })
194
176
}
195
177
196
178
// WithSSEEndpoint sets the SSE endpoint path
197
179
func WithSSEEndpoint (endpoint string ) SSEOption {
198
- return func (s * SSEServer ) {
180
+ return sseOption ( func (s * SSEServer ) {
199
181
s .sseEndpoint = endpoint
200
- }
201
- }
202
-
203
- // WithHTTPServer sets the HTTP server instance
204
- func WithHTTPServer (srv * http.Server ) SSEOption {
205
- return func (s * SSEServer ) {
206
- s .srv = srv
207
- }
208
- }
209
-
210
- func WithKeepAliveInterval (keepAliveInterval time.Duration ) SSEOption {
211
- return func (s * SSEServer ) {
212
- s .keepAlive = true
213
- s .keepAliveInterval = keepAliveInterval
214
- }
215
- }
216
-
217
- func WithKeepAlive (keepAlive bool ) SSEOption {
218
- return func (s * SSEServer ) {
219
- s .keepAlive = keepAlive
220
- }
182
+ })
221
183
}
222
184
223
185
// WithSSEContextFunc sets a function that will be called to customise the context
224
186
// to the server using the incoming request.
187
+ //
188
+ // Deprecated: Use WithContextFunc instead. This will be removed in a future version.
189
+ //
190
+ //go:deprecated
225
191
func WithSSEContextFunc (fn SSEContextFunc ) SSEOption {
226
- return func (s * SSEServer ) {
227
- s . contextFunc = fn
228
- }
192
+ return sseOption ( func (s * SSEServer ) {
193
+ WithHTTPContextFunc ( HTTPContextFunc ( fn )). applyToSSE ( s )
194
+ })
229
195
}
230
196
231
197
// NewSSEServer creates a new SSE server instance with the given MCP server and options.
@@ -241,16 +207,15 @@ func NewSSEServer(server *MCPServer, opts ...SSEOption) *SSEServer {
241
207
242
208
// Apply all options
243
209
for _ , opt := range opts {
244
- opt (s )
210
+ opt . applyToSSE (s )
245
211
}
246
212
247
213
return s
248
214
}
249
215
250
- // NewTestServer creates a test server for testing purposes
216
+ // NewTestServer creates a test server for testing purposes.
251
217
func NewTestServer (server * MCPServer , opts ... SSEOption ) * httptest.Server {
252
218
sseServer := NewSSEServer (server , opts ... )
253
-
254
219
testServer := httptest .NewServer (sseServer )
255
220
sseServer .baseURL = testServer .URL
256
221
return testServer
0 commit comments