Skip to content

Commit 876bf2e

Browse files
authored
Remove SSE support (#37)
Remove legacy SSE support, simplify data contract
1 parent 351752b commit 876bf2e

File tree

4 files changed

+6
-135
lines changed

4 files changed

+6
-135
lines changed

README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
- [Architecture](#architecture)
1010
- [Features](#features)
1111
- [Getting Started – Local Deployment](#getting-started---local-deployment)
12-
- [Getting Started – Deploy to Azure](#getting-started---deploy-to-azure)
12+
- [Getting Started – 1-Click Deploy to Azure](#getting-started---deploy-to-azure)
1313

1414
## Overview
1515

@@ -23,7 +23,7 @@ This project provides:
2323

2424
## Key Concepts
2525

26-
- **MCP Server**: A server implementing the Model Context Protocol, which typically exposes SSE or streamable HTTP endpoints.
26+
- **MCP Server**: A server implementing the Model Context Protocol, which typically a streamable HTTP endpoint.
2727
- **Adapters**: Logical resources representing MCP servers in the gateway, managed under the `/adapters` scope. Designed to coexist with other resource types (e.g., `/agents`) in a unified AI development platform.
2828
- **Session-Aware Stateful Routing**: Ensures that all requests with a given `session_id` are consistently routed to the same MCP server instance.
2929

@@ -81,8 +81,6 @@ flowchart LR
8181

8282
### Data Plane – Gateway Routing for MCP Servers
8383

84-
- `GET /adapters/{name}/sse` — Establish an initial SSE connection.
85-
- `POST /adapters/{name}/messages` — Send subsequent requests using `session_id`.
8684
- `POST /adapters/{name}/mcp` — Establish a streamable HTTP connection.
8785

8886
### Additional Capabilities
@@ -167,7 +165,6 @@ kubectl port-forward -n adapter svc/mcpgateway-service 8000:8000
167165

168166
- For other servers:
169167
- `http://localhost:8000/adapters/{name}/mcp` (Streamable HTTP)
170-
- `http://localhost:8000/adapters/{name}/sse` (SSE)
171168

172169
### 9. Clean the Environment
173170
To remove all deployed resources, delete the Kubernetes namespace:

dotnet/Microsoft.McpGateway.Management/src/Contracts/AdapterData.cs

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace Microsoft.McpGateway.Management.Contracts
88
{
99
/// <summary>
10-
/// This class represents the data for an adapter, including its name, image details, protocol, connection type, environment variables, and description.
10+
/// This class represents the data for an adapter, including its name, image details, environment variables, and description.
1111
/// </summary>
1212
public class AdapterData
1313
{
@@ -30,18 +30,6 @@ public class AdapterData
3030
[JsonPropertyOrder(3)]
3131
public required string ImageVersion { get; set; }
3232

33-
/// <summary>
34-
/// The protocol used by the adapter. Default is MCP.
35-
/// </summary>
36-
[JsonPropertyOrder(6)]
37-
public ServerProtocol Protocol { get; set; } = ServerProtocol.MCP;
38-
39-
/// <summary>
40-
/// The connection type used by the adapter. Default is SSE.
41-
/// </summary>
42-
[JsonPropertyOrder(7)]
43-
public ConnectionType ConnectionType { get; set; } = ConnectionType.StreamableHttp;
44-
4533
/// <summary>
4634
/// Environment key variables in M3 service for the adapter.
4735
/// </summary>
@@ -57,13 +45,13 @@ public class AdapterData
5745
/// <summary>
5846
/// A description of the adapter.
5947
/// </summary>
60-
[JsonPropertyOrder(8)]
48+
[JsonPropertyOrder(6)]
6149
public string Description { get; set; } = string.Empty;
6250

6351
/// <summary>
6452
/// Indicates whether to use workload identity for the deployed adapter instance. Default is false.
6553
/// </summary>
66-
[JsonPropertyOrder(9)]
54+
[JsonPropertyOrder(7)]
6755
public bool UseWorkloadIdentity { get; set; } = false;
6856

6957
public AdapterData(
@@ -73,8 +61,6 @@ public AdapterData(
7361
Dictionary<string, string>? environmentVariables = null,
7462
int? replicaCount = 1,
7563
string description = "",
76-
ServerProtocol protocol = ServerProtocol.MCP,
77-
ConnectionType connectionType = ConnectionType.StreamableHttp,
7864
bool useWorkloadIdentity = false)
7965
{
8066
ArgumentException.ThrowIfNullOrEmpty(name);
@@ -84,8 +70,6 @@ public AdapterData(
8470
Name = name;
8571
ImageName = imageName;
8672
ImageVersion = imageVersion;
87-
Protocol = protocol;
88-
ConnectionType = connectionType;
8973
EnvironmentVariables = environmentVariables ?? [];
9074
ReplicaCount = replicaCount ?? 1;
9175
Description = description;
@@ -94,17 +78,4 @@ public AdapterData(
9478

9579
public AdapterData() { }
9680
}
97-
98-
[JsonConverter(typeof(JsonStringEnumConverter))]
99-
public enum ConnectionType
100-
{
101-
SSE,
102-
StreamableHttp,
103-
}
104-
105-
[JsonConverter(typeof(JsonStringEnumConverter))]
106-
public enum ServerProtocol
107-
{
108-
MCP
109-
}
11081
}

dotnet/Microsoft.McpGateway.Management/src/Contracts/AdapterResource.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class AdapterResource : AdapterData
3030
public DateTimeOffset LastUpdatedAt { get; set; }
3131

3232
public AdapterResource(AdapterData adapterData, string createdBy, DateTimeOffset createdAt, DateTimeOffset lastUpdatedAt)
33-
: base(adapterData.Name, adapterData.ImageName, adapterData.ImageVersion, adapterData.EnvironmentVariables, adapterData.ReplicaCount, adapterData.Description, adapterData.Protocol, adapterData.ConnectionType, adapterData.UseWorkloadIdentity)
33+
: base(adapterData.Name, adapterData.ImageName, adapterData.ImageVersion, adapterData.EnvironmentVariables, adapterData.ReplicaCount, adapterData.Description, adapterData.UseWorkloadIdentity)
3434
{
3535
CreatedBy = createdBy;
3636
CreatedAt = createdAt;
@@ -47,8 +47,6 @@ public static AdapterResource Create(AdapterData data, string createdBy, DateTim
4747
EnvironmentVariables = data.EnvironmentVariables,
4848
ReplicaCount = data.ReplicaCount,
4949
Description = data.Description,
50-
Protocol = data.Protocol,
51-
ConnectionType = data.ConnectionType,
5250
CreatedBy = createdBy,
5351
CreatedAt = createdAt,
5452
LastUpdatedAt = DateTime.UtcNow,

dotnet/Microsoft.McpGateway.Service/src/Controllers/AdapterReverseProxyController.cs

Lines changed: 0 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ namespace Microsoft.McpGateway.Service.Controllers
1313
[Authorize]
1414
public class AdapterReverseProxyController(IHttpClientFactory httpClientFactory, IAdapterSessionStore sessionStore, ISessionRoutingHandler sessionRoutingHandler) : ControllerBase
1515
{
16-
private const string MCPSSEMarker = "/messages/?session_id=";
1716
private readonly IHttpClientFactory httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
1817
private readonly IAdapterSessionStore sessionStore = sessionStore ?? throw new ArgumentNullException(nameof(sessionStore));
1918
private readonly ISessionRoutingHandler sessionRoutingHandler = sessionRoutingHandler ?? throw new ArgumentNullException(nameof(sessionRoutingHandler));
@@ -52,100 +51,6 @@ public async Task ForwardStreamableHttpRequest(string name, CancellationToken ca
5251
await HttpProxy.CopyProxiedHttpResponseAsync(HttpContext, response, cancellationToken).ConfigureAwait(false);
5352
}
5453

55-
/// <summary>
56-
/// Support for the legacy MCP SSE connection.
57-
/// </summary>
58-
[HttpPost("{name}/messages")]
59-
public async Task ForwardRequest(CancellationToken cancellationToken)
60-
{
61-
var targetAddress = await sessionRoutingHandler.GetExistingSessionTargetAsync(HttpContext, cancellationToken).ConfigureAwait(false);
62-
63-
if (targetAddress == null)
64-
{
65-
HttpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
66-
return;
67-
}
68-
69-
using var client = httpClientFactory.CreateClient(Constants.HttpClientNames.AdapterProxyClient);
70-
var proxiedRequest = HttpProxy.CreateProxiedHttpRequest(HttpContext, (uri) => ReplaceUriAddress(uri, targetAddress));
71-
72-
var response = await client.SendAsync(proxiedRequest, cancellationToken).ConfigureAwait(false);
73-
74-
await HttpProxy.CopyProxiedHttpResponseAsync(HttpContext, response, cancellationToken).ConfigureAwait(false);
75-
}
76-
77-
/// <summary>
78-
/// Support for the legacy MCP SSE connection.
79-
/// </summary>
80-
[HttpGet("{name}/sse")]
81-
public async Task ForwardSseRequest(string name, CancellationToken cancellationToken)
82-
{
83-
var targetAddress = await sessionRoutingHandler.GetNewSessionTargetAsync(name, HttpContext, cancellationToken).ConfigureAwait(false);
84-
85-
if (targetAddress == null)
86-
{
87-
HttpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
88-
return;
89-
}
90-
91-
var proxiedRequest = HttpProxy.CreateProxiedHttpRequest(HttpContext, (uri) => ReplaceUriAddress(uri, targetAddress));
92-
93-
using var client = httpClientFactory.CreateClient(Constants.HttpClientNames.AdapterProxyClient);
94-
var response = await client.SendAsync(proxiedRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
95-
96-
await StreamProxiedSseHttpResponseAsync(HttpContext, name, response, targetAddress, cancellationToken).ConfigureAwait(false);
97-
}
98-
99-
private async Task StreamProxiedSseHttpResponseAsync(HttpContext context, string name, HttpResponseMessage response, string targetAddress, CancellationToken cancellationToken)
100-
{
101-
context.Response.StatusCode = (int)response.StatusCode;
102-
103-
foreach (var header in response.Headers)
104-
context.Response.Headers[header.Key] = header.Value.ToArray();
105-
foreach (var header in response.Content.Headers)
106-
context.Response.Headers[header.Key] = header.Value.ToArray();
107-
108-
context.Response.Headers.Remove("transfer-encoding");
109-
context.Features.Get<AspNetCore.Http.Features.IHttpResponseBodyFeature>()?.DisableBuffering();
110-
111-
using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
112-
113-
var buffer = new byte[8192];
114-
string? sessionId = null;
115-
while (!cancellationToken.IsCancellationRequested)
116-
{
117-
var chunkLength = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken).ConfigureAwait(false);
118-
if (chunkLength > 0)
119-
{
120-
// There's an issue that the server response from the initialization call does not respect the base url.
121-
// Before awaiting fix from MCP, having gateway to rewrite the response from the MCP server during the initialization phrase to append the prefix routing.
122-
if (sessionId == null)
123-
{
124-
var chunkValue = Encoding.UTF8.GetString(buffer, 0, chunkLength);
125-
var index = chunkValue.IndexOf(MCPSSEMarker, StringComparison.OrdinalIgnoreCase);
126-
if (index >= 0)
127-
{
128-
var modified = chunkValue.Replace(MCPSSEMarker, $"/adapters/{name}{MCPSSEMarker}");
129-
sessionId = chunkValue[(index + MCPSSEMarker.Length)..].Trim();
130-
131-
if (!string.IsNullOrEmpty(sessionId))
132-
{
133-
await sessionStore.SetAsync(sessionId, targetAddress, cancellationToken).ConfigureAwait(false);
134-
}
135-
136-
await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(modified), cancellationToken).ConfigureAwait(false);
137-
}
138-
}
139-
else
140-
{
141-
await context.Response.Body.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
142-
}
143-
144-
await context.Response.Body.FlushAsync(cancellationToken).ConfigureAwait(false);
145-
}
146-
}
147-
}
148-
14954
private static Uri ReplaceUriAddress(Uri originalUri, string newAddress)
15055
{
15156
ArgumentNullException.ThrowIfNull(originalUri);

0 commit comments

Comments
 (0)